@yuants/vendor-gate 0.7.4 → 0.8.0

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 (38) hide show
  1. package/dist/api/http-client.js +58 -17
  2. package/dist/api/http-client.js.map +1 -1
  3. package/dist/api/private-api.js +28 -0
  4. package/dist/api/private-api.js.map +1 -1
  5. package/dist/api/public-api.js +20 -0
  6. package/dist/api/public-api.js.map +1 -1
  7. package/dist/services/accounts/earning.js +61 -0
  8. package/dist/services/accounts/earning.js.map +1 -0
  9. package/dist/services/accounts/earning.test.js +117 -0
  10. package/dist/services/accounts/earning.test.js.map +1 -0
  11. package/dist/services/exchange.js +13 -7
  12. package/dist/services/exchange.js.map +1 -1
  13. package/lib/api/http-client.d.ts.map +1 -1
  14. package/lib/api/http-client.js +58 -17
  15. package/lib/api/http-client.js.map +1 -1
  16. package/lib/api/private-api.d.ts +22 -0
  17. package/lib/api/private-api.d.ts.map +1 -1
  18. package/lib/api/private-api.js +30 -1
  19. package/lib/api/private-api.js.map +1 -1
  20. package/lib/api/public-api.d.ts +7 -0
  21. package/lib/api/public-api.d.ts.map +1 -1
  22. package/lib/api/public-api.js +22 -1
  23. package/lib/api/public-api.js.map +1 -1
  24. package/lib/services/accounts/earning.d.ts +7 -0
  25. package/lib/services/accounts/earning.d.ts.map +1 -0
  26. package/lib/services/accounts/earning.js +65 -0
  27. package/lib/services/accounts/earning.js.map +1 -0
  28. package/lib/services/accounts/earning.test.d.ts +2 -0
  29. package/lib/services/accounts/earning.test.d.ts.map +1 -0
  30. package/lib/services/accounts/earning.test.js +152 -0
  31. package/lib/services/accounts/earning.test.js.map +1 -0
  32. package/lib/services/exchange.js +13 -7
  33. package/lib/services/exchange.js.map +1 -1
  34. package/package.json +5 -5
  35. package/temp/build/typescript/ts_Eujh_fX2.json +1 -1
  36. package/temp/package-deps.json +10 -8
  37. package/temp/test/jest/haste-map-ac984f375ffe2771b37aa116ca7fe6f5-87fb4bb14df3d29fb61369ce8cbe1fe1-6499ab22de0ac9ce813ded0e96430273 +0 -0
  38. package/temp/test/jest/perf-cache-ac984f375ffe2771b37aa116ca7fe6f5-da39a3ee5e6b4b0d3255bfef95601890 +1 -0
@@ -37,10 +37,21 @@ const parseJSON = async (response, path, params) => {
37
37
  return JSON.parse(text);
38
38
  }
39
39
  catch (error) {
40
- console.info(formatTime(Date.now()), 'GateRequestFailed', path, JSON.stringify(params !== null && params !== void 0 ? params : {}), text, {
41
- status: response.status,
42
- headers: toHeaderObject(response.headers),
43
- });
40
+ // 只在 DEBUG 模式下打印完整响应文本,避免泄露敏感信息
41
+ if (process.env.LOG_LEVEL === 'DEBUG') {
42
+ console.debug(formatTime(Date.now()), 'GateRequestFailed', path, JSON.stringify(params !== null && params !== void 0 ? params : {}), text, {
43
+ status: response.status,
44
+ headers: toHeaderObject(response.headers),
45
+ });
46
+ }
47
+ else {
48
+ // 非 DEBUG 模式下只打印摘要
49
+ const textPreview = text.length > 100 ? text.substring(0, 100) + '...' : text;
50
+ console.info(formatTime(Date.now()), 'GateRequestFailed', path, JSON.stringify(params !== null && params !== void 0 ? params : {}), textPreview, {
51
+ status: response.status,
52
+ headers: toHeaderObject(response.headers),
53
+ });
54
+ }
44
55
  throw error;
45
56
  }
46
57
  };
@@ -53,12 +64,24 @@ export const requestPublic = async (method, path, params) => {
53
64
  headers['Content-Type'] = 'application/json';
54
65
  }
55
66
  console.info(formatTime(Date.now()), method, url.href);
56
- const response = await fetch(url.href, {
57
- method,
58
- headers,
59
- body: body || undefined,
60
- });
61
- return parseJSON(response, path, params);
67
+ // 添加请求超时控制(30秒)
68
+ const timeoutMs = 30000;
69
+ const abortController = new AbortController();
70
+ const timeoutId = setTimeout(() => {
71
+ abortController.abort(new Error(`Request timeout after ${timeoutMs}ms`));
72
+ }, timeoutMs);
73
+ try {
74
+ const response = await fetch(url.href, {
75
+ method,
76
+ headers,
77
+ body: body || undefined,
78
+ signal: abortController.signal,
79
+ });
80
+ return await parseJSON(response, path, params);
81
+ }
82
+ finally {
83
+ clearTimeout(timeoutId);
84
+ }
62
85
  };
63
86
  export const requestPrivate = async (credential, method, path, params) => {
64
87
  const { url, body } = createRequestArtifacts(method, path, params);
@@ -77,12 +100,30 @@ export const requestPrivate = async (credential, method, path, params) => {
77
100
  if (process.env.CHANNEL_ID) {
78
101
  headers['X-Gate-Channel-Id'] = process.env.CHANNEL_ID;
79
102
  }
80
- console.info(formatTime(Date.now()), method, url.href, JSON.stringify(headers), body);
81
- const response = await fetch(url.href, {
82
- method,
83
- headers,
84
- body: body || undefined,
85
- });
86
- return parseJSON(response, path, params);
103
+ // 安全日志:过滤敏感头信息
104
+ const safeHeaders = Object.assign({}, headers);
105
+ if (safeHeaders.KEY)
106
+ safeHeaders.KEY = '***';
107
+ if (safeHeaders.SIGN)
108
+ safeHeaders.SIGN = '***';
109
+ console.info(formatTime(Date.now()), method, url.href, JSON.stringify(safeHeaders), body);
110
+ // 添加请求超时控制(30秒)
111
+ const timeoutMs = 30000;
112
+ const abortController = new AbortController();
113
+ const timeoutId = setTimeout(() => {
114
+ abortController.abort(new Error(`Request timeout after ${timeoutMs}ms`));
115
+ }, timeoutMs);
116
+ try {
117
+ const response = await fetch(url.href, {
118
+ method,
119
+ headers,
120
+ body: body || undefined,
121
+ signal: abortController.signal,
122
+ });
123
+ return await parseJSON(response, path, params);
124
+ }
125
+ finally {
126
+ clearTimeout(timeoutId);
127
+ }
87
128
  };
88
129
  //# sourceMappingURL=http-client.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"http-client.js","sourceRoot":"","sources":["../../src/api/http-client.ts"],"names":[],"mappings":"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","sourcesContent":["import { encodeHex, formatTime, HmacSHA512, sha512 } from '@yuants/utils';\nimport { join } from 'path';\n\nconst BASE_URL = 'https://api.gateio.ws/api/v4';\n\nexport type HttpMethod = 'GET' | 'POST' | 'DELETE' | 'PUT';\n\nexport type GateParams = Record<string, string | number | boolean | undefined>;\n\nexport interface IGateCredential {\n access_key: string;\n secret_key: string;\n}\n\ninterface IRequestArtifacts {\n url: URL;\n body: string;\n}\n\nconst serializeQueryParams = (params?: GateParams): Record<string, string> | undefined => {\n if (!params) return undefined;\n const normalizedEntries = Object.entries(params).filter(([, value]) => value !== undefined);\n if (normalizedEntries.length === 0) {\n return undefined;\n }\n return Object.fromEntries(normalizedEntries.map(([key, value]) => [key, `${value}`]));\n};\n\nconst createRequestArtifacts = (method: HttpMethod, path: string, params?: GateParams): IRequestArtifacts => {\n const url = new URL(BASE_URL);\n url.pathname = join(url.pathname, path);\n const searchParams = serializeQueryParams(params);\n if (method === 'GET' && searchParams) {\n Object.entries(searchParams).forEach(([key, value]) => url.searchParams.set(key, value));\n }\n const rawBody = method === 'GET' ? '' : JSON.stringify(params);\n const body = rawBody;\n return { url, body };\n};\n\nconst toHeaderObject = (headers: Headers): Record<string, string> => {\n const iterable = headers as unknown as Iterable<[string, string]>;\n return Object.fromEntries(Array.from(iterable));\n};\n\nconst parseJSON = async <TResponse>(\n response: Response,\n path: string,\n params?: GateParams,\n): Promise<TResponse> => {\n const text = await response.text();\n if (process.env.LOG_LEVEL === 'DEBUG') {\n console.debug(formatTime(Date.now()), 'GateResponse', path, JSON.stringify(params ?? {}), text, {\n status: response.status,\n headers: toHeaderObject(response.headers),\n });\n }\n try {\n return JSON.parse(text) as TResponse;\n } catch (error) {\n console.info(formatTime(Date.now()), 'GateRequestFailed', path, JSON.stringify(params ?? {}), text, {\n status: response.status,\n headers: toHeaderObject(response.headers),\n });\n throw error;\n }\n};\n\nexport const requestPublic = async <TResponse>(\n method: HttpMethod,\n path: string,\n params?: GateParams,\n): Promise<TResponse> => {\n const { url, body } = createRequestArtifacts(method, path, params);\n const headers: Record<string, string> = {\n Accept: 'application/json',\n };\n if (body) {\n headers['Content-Type'] = 'application/json';\n }\n console.info(formatTime(Date.now()), method, url.href);\n const response = await fetch(url.href, {\n method,\n headers,\n body: body || undefined,\n });\n return parseJSON<TResponse>(response, path, params);\n};\n\nexport const requestPrivate = async <TResponse>(\n credential: IGateCredential,\n method: HttpMethod,\n path: string,\n params?: GateParams,\n): Promise<TResponse> => {\n const { url, body } = createRequestArtifacts(method, path, params);\n const timestamp = Date.now() / 1000;\n\n const bodyDigest = encodeHex(await sha512(new TextEncoder().encode(body)));\n const signTarget = `${method}\\n${url.pathname}\\n${url.searchParams}\\n${bodyDigest}\\n${timestamp}`;\n const sign = encodeHex(\n await HmacSHA512(new TextEncoder().encode(signTarget), new TextEncoder().encode(credential.secret_key)),\n );\n\n const headers: Record<string, string> = {\n Accept: 'application/json',\n 'Content-Type': 'application/json',\n KEY: credential.access_key,\n SIGN: sign,\n Timestamp: `${timestamp}`,\n };\n\n // Add Channel ID header if exists\n if (process.env.CHANNEL_ID) {\n headers['X-Gate-Channel-Id'] = process.env.CHANNEL_ID;\n }\n\n console.info(formatTime(Date.now()), method, url.href, JSON.stringify(headers), body);\n\n const response = await fetch(url.href, {\n method,\n headers,\n body: body || undefined,\n });\n return parseJSON<TResponse>(response, path, params);\n};\n"]}
1
+ {"version":3,"file":"http-client.js","sourceRoot":"","sources":["../../src/api/http-client.ts"],"names":[],"mappings":"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","sourcesContent":["import { encodeHex, formatTime, HmacSHA512, sha512 } from '@yuants/utils';\nimport { join } from 'path';\n\nconst BASE_URL = 'https://api.gateio.ws/api/v4';\n\nexport type HttpMethod = 'GET' | 'POST' | 'DELETE' | 'PUT';\n\nexport type GateParams = Record<string, string | number | boolean | undefined>;\n\nexport interface IGateCredential {\n access_key: string;\n secret_key: string;\n}\n\ninterface IRequestArtifacts {\n url: URL;\n body: string;\n}\n\nconst serializeQueryParams = (params?: GateParams): Record<string, string> | undefined => {\n if (!params) return undefined;\n const normalizedEntries = Object.entries(params).filter(([, value]) => value !== undefined);\n if (normalizedEntries.length === 0) {\n return undefined;\n }\n return Object.fromEntries(normalizedEntries.map(([key, value]) => [key, `${value}`]));\n};\n\nconst createRequestArtifacts = (method: HttpMethod, path: string, params?: GateParams): IRequestArtifacts => {\n const url = new URL(BASE_URL);\n url.pathname = join(url.pathname, path);\n const searchParams = serializeQueryParams(params);\n if (method === 'GET' && searchParams) {\n Object.entries(searchParams).forEach(([key, value]) => url.searchParams.set(key, value));\n }\n const rawBody = method === 'GET' ? '' : JSON.stringify(params);\n const body = rawBody;\n return { url, body };\n};\n\nconst toHeaderObject = (headers: Headers): Record<string, string> => {\n const iterable = headers as unknown as Iterable<[string, string]>;\n return Object.fromEntries(Array.from(iterable));\n};\n\nconst parseJSON = async <TResponse>(\n response: Response,\n path: string,\n params?: GateParams,\n): Promise<TResponse> => {\n const text = await response.text();\n if (process.env.LOG_LEVEL === 'DEBUG') {\n console.debug(formatTime(Date.now()), 'GateResponse', path, JSON.stringify(params ?? {}), text, {\n status: response.status,\n headers: toHeaderObject(response.headers),\n });\n }\n try {\n return JSON.parse(text) as TResponse;\n } catch (error) {\n // 只在 DEBUG 模式下打印完整响应文本,避免泄露敏感信息\n if (process.env.LOG_LEVEL === 'DEBUG') {\n console.debug(formatTime(Date.now()), 'GateRequestFailed', path, JSON.stringify(params ?? {}), text, {\n status: response.status,\n headers: toHeaderObject(response.headers),\n });\n } else {\n // 非 DEBUG 模式下只打印摘要\n const textPreview = text.length > 100 ? text.substring(0, 100) + '...' : text;\n console.info(\n formatTime(Date.now()),\n 'GateRequestFailed',\n path,\n JSON.stringify(params ?? {}),\n textPreview,\n {\n status: response.status,\n headers: toHeaderObject(response.headers),\n },\n );\n }\n throw error;\n }\n};\n\nexport const requestPublic = async <TResponse>(\n method: HttpMethod,\n path: string,\n params?: GateParams,\n): Promise<TResponse> => {\n const { url, body } = createRequestArtifacts(method, path, params);\n const headers: Record<string, string> = {\n Accept: 'application/json',\n };\n if (body) {\n headers['Content-Type'] = 'application/json';\n }\n console.info(formatTime(Date.now()), method, url.href);\n\n // 添加请求超时控制(30秒)\n const timeoutMs = 30_000;\n const abortController = new AbortController();\n const timeoutId = setTimeout(() => {\n abortController.abort(new Error(`Request timeout after ${timeoutMs}ms`));\n }, timeoutMs);\n\n try {\n const response = await fetch(url.href, {\n method,\n headers,\n body: body || undefined,\n signal: abortController.signal,\n });\n return await parseJSON<TResponse>(response, path, params);\n } finally {\n clearTimeout(timeoutId);\n }\n};\n\nexport const requestPrivate = async <TResponse>(\n credential: IGateCredential,\n method: HttpMethod,\n path: string,\n params?: GateParams,\n): Promise<TResponse> => {\n const { url, body } = createRequestArtifacts(method, path, params);\n const timestamp = Date.now() / 1000;\n\n const bodyDigest = encodeHex(await sha512(new TextEncoder().encode(body)));\n const signTarget = `${method}\\n${url.pathname}\\n${url.searchParams}\\n${bodyDigest}\\n${timestamp}`;\n const sign = encodeHex(\n await HmacSHA512(new TextEncoder().encode(signTarget), new TextEncoder().encode(credential.secret_key)),\n );\n\n const headers: Record<string, string> = {\n Accept: 'application/json',\n 'Content-Type': 'application/json',\n KEY: credential.access_key,\n SIGN: sign,\n Timestamp: `${timestamp}`,\n };\n\n // Add Channel ID header if exists\n if (process.env.CHANNEL_ID) {\n headers['X-Gate-Channel-Id'] = process.env.CHANNEL_ID;\n }\n\n // 安全日志:过滤敏感头信息\n const safeHeaders = { ...headers };\n if (safeHeaders.KEY) safeHeaders.KEY = '***';\n if (safeHeaders.SIGN) safeHeaders.SIGN = '***';\n console.info(formatTime(Date.now()), method, url.href, JSON.stringify(safeHeaders), body);\n\n // 添加请求超时控制(30秒)\n const timeoutMs = 30_000;\n const abortController = new AbortController();\n const timeoutId = setTimeout(() => {\n abortController.abort(new Error(`Request timeout after ${timeoutMs}ms`));\n }, timeoutMs);\n\n try {\n const response = await fetch(url.href, {\n method,\n headers,\n body: body || undefined,\n signal: abortController.signal,\n });\n return await parseJSON<TResponse>(response, path, params);\n } finally {\n clearTimeout(timeoutId);\n }\n};\n"]}
@@ -12,6 +12,34 @@ export const getAccountDetail = (credential) => callPrivate(credential, 'GET', '
12
12
  * https://www.gate.com/docs/developers/apiv4/zh_CN/#%E8%8E%B7%E5%8F%96%E7%BB%9F%E4%B8%80%E8%B4%A6%E6%88%B7%E4%BF%A1%E6%81%AF
13
13
  */
14
14
  export const getUnifiedAccounts = (credential, params) => callPrivate(credential, 'GET', '/unified/accounts', params);
15
+ /**
16
+ * 获取用户理财余额(Gate.io EarnUni 余币宝理财)
17
+ *
18
+ * https://www.gate.com/docs/developers/apiv4/zh_CN/#查询用户币种理财列表
19
+ * 端点:GET /earn/uni/lends(需要鉴权)
20
+ */
21
+ export const getEarnBalance = (credential, params) => {
22
+ // 输入参数校验
23
+ const validatedParams = Object.assign({}, params);
24
+ if (validatedParams.currency !== undefined) {
25
+ if (typeof validatedParams.currency !== 'string' || !/^[A-Za-z0-9]+$/.test(validatedParams.currency)) {
26
+ throw new Error(`Invalid currency format: ${validatedParams.currency}`);
27
+ }
28
+ }
29
+ if (validatedParams.page !== undefined) {
30
+ if (!Number.isInteger(validatedParams.page) || validatedParams.page < 1) {
31
+ throw new Error(`Invalid page: must be positive integer, got ${validatedParams.page}`);
32
+ }
33
+ }
34
+ if (validatedParams.limit !== undefined) {
35
+ if (!Number.isInteger(validatedParams.limit) ||
36
+ validatedParams.limit < 1 ||
37
+ validatedParams.limit > 1000) {
38
+ throw new Error(`Invalid limit: must be between 1 and 1000, got ${validatedParams.limit}`);
39
+ }
40
+ }
41
+ return callPrivate(credential, 'GET', '/earn/uni/lends', validatedParams);
42
+ };
15
43
  /**
16
44
  * 获取用户仓位列表
17
45
  *
@@ -1 +1 @@
1
- {"version":3,"file":"private-api.js","sourceRoot":"","sources":["../../src/api/private-api.ts"],"names":[],"mappings":"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","sourcesContent":["import type { GateParams, HttpMethod, IGateCredential } from './http-client';\nimport { requestPrivate } from './http-client';\n\nexport type ICredential = IGateCredential;\n\nconst callPrivate = <TResponse>(\n credential: ICredential,\n method: HttpMethod,\n path: string,\n params?: GateParams,\n) => requestPrivate<TResponse>(credential, method, path, params);\n\n/**\n * 获取用户账户信息\n *\n * https://www.gate.io/docs/developers/apiv4/zh_CN/#%E8%8E%B7%E5%8F%96%E7%94%A8%E6%88%B7%E8%B4%A6%E6%88%B7%E4%BF%A1%E6%81%AF\n */\nexport const getAccountDetail = (\n credential: ICredential,\n): Promise<{\n user_id: number;\n ip_whitelist: string[];\n currency_pairs: string[];\n key: {\n mode: number;\n };\n tier: number;\n}> => callPrivate(credential, 'GET', '/account/detail');\n\n/**\n * 获取统一账户信息\n *\n * https://www.gate.com/docs/developers/apiv4/zh_CN/#%E8%8E%B7%E5%8F%96%E7%BB%9F%E4%B8%80%E8%B4%A6%E6%88%B7%E4%BF%A1%E6%81%AF\n */\nexport const getUnifiedAccounts = (\n credential: ICredential,\n params?: { currency?: string },\n): Promise<{\n user_id: string;\n refresh_time: number;\n locket: boolean;\n balances: Record<\n string,\n {\n available: string;\n freeze: string;\n borrowed: string;\n negative_liab: string;\n futures_pos_liab: string;\n equity: string;\n total_freeze: string;\n total_liab: string;\n spot_in_use: string;\n cross_balance: string;\n iso_balance: string;\n funding: string;\n }\n >;\n total: string;\n borrowed: string;\n total_initial_margin: string;\n total_margin_balance: string;\n total_maintenance_margin: string;\n total_initial_margin_rate: string;\n total_maintenance_margin_rate: string;\n total_avail_margin: string;\n unified_account_total: string;\n unified_account_total_liab: string;\n unified_account_total_equity: string;\n leverage: string;\n spot_order_loss: string;\n spot_hedge: boolean;\n}> => callPrivate(credential, 'GET', '/unified/accounts', params);\n\n/**\n * 获取用户仓位列表\n *\n * https://www.gate.io/docs/developers/apiv4/zh_CN/#%E8%8E%B7%E5%8F%96%E7%94%A8%E6%88%B7%E4%BB%93%E4%BD%8D%E5%88%97%E8%A1%A8\n */\nexport const getFuturePositions = (\n credential: ICredential,\n quote_currency: string,\n params?: { holding?: boolean; limit?: number; offset?: number },\n): Promise<\n {\n user: number;\n contract: string;\n size: number;\n leverage: string;\n risk_limit: string;\n leverage_max: string;\n maintenance_rate: string;\n value: string;\n margin: string;\n entry_price: string;\n mark_price: string;\n unrealised_pnl: string;\n realised_pnl: string;\n mode: string;\n liq_price: string;\n }[]\n> => callPrivate(credential, 'GET', `/futures/${quote_currency}/positions`, params);\n\n/**\n * 查询合约订单列表\n *\n * https://www.gate.io/docs/developers/apiv4/zh_CN/#%E6%9F%A5%E8%AF%A2%E5%90%88%E7%BA%A6%E8%AE%A2%E5%8D%95%E5%88%97%E8%A1%A8\n */\nexport const getFuturesOrders = (\n credential: ICredential,\n settle: string,\n params: {\n contract?: string;\n status: string;\n limit?: number;\n offset?: number;\n last_id?: number;\n },\n): Promise<\n {\n id: string;\n contract: string;\n create_time: number;\n size: number;\n price: string;\n is_close: boolean;\n fill_price: string;\n left?: number;\n status?: string;\n text: string;\n }[]\n> => callPrivate(credential, 'GET', `/futures/${settle}/orders`, params);\n\n/**\n * 获取合约账号\n *\n * https://www.gate.io/docs/developers/apiv4/zh_CN/#%E8%8E%B7%E5%8F%96%E5%90%88%E7%BA%A6%E8%B4%A6%E5%8F%B7\n */\nexport const getFuturesAccounts = (\n credential: ICredential,\n settle: string,\n): Promise<{\n user: number;\n currency: string;\n total: string;\n unrealised_pnl: string;\n position_margin: string;\n order_margin: string;\n available: string;\n point: string;\n bonus: string;\n in_dual_mode: boolean;\n enable_evolved_classic: boolean;\n history: {\n dnw: string;\n pnl: string;\n fee: string;\n refr: string;\n fund: string;\n point_dnw: string;\n point_fee: string;\n point_refr: string;\n bonus_dnw: string;\n bonus_offset: string;\n };\n}> => callPrivate(credential, 'GET', `/futures/${settle}/accounts`);\n\n/**\n * 合约交易下单\n *\n * 下单时指定的是合约张数 size ,而非币的数量,每一张合约对应的币的数量是合约详情接口里返回的 quanto_multiplier\n *\n * 0 成交的订单在撤单 10 分钟之后无法再获取到,会提到订单不存在\n *\n * 设置 reduce_only 为 true 可以防止在减仓的时候穿仓\n *\n * 单仓模式下,如果需要平仓,需要设置 size 为 0 ,close 为 true\n *\n * 双仓模式下,平仓需要使用 auto_size 来设置平仓方向,并同时设置 reduce_only 为 true,size 为 0\n *\n * 设置 stp_act 决定使用限制用户自成交的策略,详细用法参考body参数stp_act\n *\n * https://www.gate.io/docs/developers/apiv4/zh_CN/#%E5%90%88%E7%BA%A6%E4%BA%A4%E6%98%93%E4%B8%8B%E5%8D%95\n */\nexport const postFutureOrders = (\n credential: ICredential,\n settle: string,\n params: {\n contract: string;\n size: number;\n iceberg?: number;\n price?: string;\n close?: boolean;\n reduce_only?: boolean;\n tif?: string;\n text?: string;\n auto_size?: string;\n stp_act?: string;\n },\n): Promise<{\n label?: string;\n message?: string;\n detail?: string;\n id: number;\n user: number;\n contract: string;\n create_time: number;\n size: number;\n iceberg: number;\n left: number;\n price: string;\n fill_price: string;\n mkfr: string;\n tkfr: string;\n tif: string;\n refu: number;\n is_reduce_only: boolean;\n is_close: boolean;\n is_liq: boolean;\n text: string;\n status: string;\n finish_time: number;\n finish_as: string;\n stp_id: number;\n stp_act: string;\n amend_text: string;\n}> => callPrivate(credential, 'POST', `/futures/${settle}/orders`, params);\n\n/**\n * 撤销单个订单\n *\n * https://www.gate.io/docs/developers/apiv4/zh_CN/#%E6%92%A4%E9%94%80%E5%8D%95%E4%B8%AA%E8%AE%A2%E5%8D%95-2\n */\nexport const deleteFutureOrders = (credential: ICredential, settle: string, order_id: string): Promise<{}> =>\n callPrivate(credential, 'DELETE', `/futures/${settle}/orders/${order_id}`);\n\n/**\n * 提现\n *\n * POST /withdrawals\n *\n * https://www.gate.io/docs/developers/apiv4/zh_CN/#%E6%8F%90%E7%8E%B0\n */\nexport const postWithdrawals = (\n credential: ICredential,\n params: {\n withdraw_order_id?: string;\n amount: string;\n currency: string;\n address?: string;\n memo?: string;\n chain: string;\n },\n): Promise<{\n id: string;\n txid: string;\n withdraw_order_id: string;\n timestamp: number;\n amount: string;\n currency: string;\n address: string;\n memo: string;\n status: string;\n chain: string;\n}> => callPrivate(credential, 'POST', '/withdrawals', params);\n\n/**\n * 获取币种充值地址\n *\n * https://www.gate.io/docs/developers/apiv4/zh_CN/#%E8%8E%B7%E5%8F%96%E5%B8%81%E7%A7%8D%E5%85%85%E5%80%BC%E5%9C%B0%E5%9D%80\n */\nexport const getDepositAddress = (\n credential: ICredential,\n params: {\n currency: string;\n },\n): Promise<{\n currency: string;\n address: string;\n multichain_addresses: {\n chain: string;\n address: string;\n payment_id: string;\n payment_name: string;\n obtain_failed: boolean;\n }[];\n}> => callPrivate(credential, 'GET', '/wallet/deposit_address', params);\n\n/**\n * 创建新的子账户\n *\n * https://www.gate.io/docs/developers/apiv4/zh_CN/#%E5%88%9B%E5%BB%BA%E6%96%B0%E7%9A%84%E5%AD%90%E8%B4%A6%E6%88%B7\n */\nexport const getSubAccountList = (\n credential: ICredential,\n params?: {\n type?: string;\n },\n): Promise<\n {\n remark: string;\n login_name: string;\n password: string;\n email: string;\n state: number;\n type: number;\n user_id: number;\n create_time: number;\n }[]\n> => callPrivate(credential, 'GET', '/sub_accounts', params);\n\n/**\n * 获取充值记录\n *\n * 记录查询时间范围不允许超过 30 天\n *\n * https://www.gate.io/docs/developers/apiv4/zh_CN/#%E8%8E%B7%E5%8F%96%E5%85%85%E5%80%BC%E8%AE%B0%E5%BD%95\n */\nexport const getDepositHistory = (\n credential: ICredential,\n params?: {\n currency?: string;\n from?: number;\n to?: number;\n limit?: number;\n offset?: number;\n },\n): Promise<\n {\n id: string;\n txid: string;\n withdraw_order_id: string;\n timestamp: number;\n amount: string;\n currency: string;\n address: string;\n memo: string;\n status: string;\n chain: string;\n }[]\n> => callPrivate(credential, 'GET', '/wallet/deposits', params);\n\n/**\n * 获取提现记录\n *\n * 记录查询时间范围不允许超过 30 天\n *\n * https://www.gate.io/docs/developers/apiv4/zh_CN/#%E8%8E%B7%E5%8F%96%E5%B8%81%E7%A7%8D%E5%85%85%E5%80%BC%E5%9C%B0%E5%9D%80\n */\nexport const getWithdrawalHistory = (\n credential: ICredential,\n params?: {\n currency?: string;\n from?: number;\n to?: number;\n limit?: number;\n offset?: number;\n },\n): Promise<\n {\n id: string;\n txid: string;\n withdraw_order_id: string;\n timestamp: number;\n amount: string;\n currency: string;\n address: string;\n memo: string;\n status: string;\n chain: string;\n }[]\n> => callPrivate(credential, 'GET', '/wallet/withdrawals', params);\n\n/**\n * 获取现货交易账户列表\n *\n * https://www.gate.io/docs/developers/apiv4/zh_CN/#%E8%8E%B7%E5%8F%96%E7%8E%B0%E8%B4%A7%E4%BA%A4%E6%98%93%E8%B4%A6%E6%88%B7%E5%88%97%E8%A1%A8\n */\nexport const getSpotAccounts = (\n credential: ICredential,\n params?: {\n currency?: string;\n },\n): Promise<\n {\n currency: string;\n available: string;\n locked: string;\n update_id: string;\n }[]\n> => callPrivate(credential, 'GET', '/spot/accounts', params);\n\n/**\n * 交易账户互转\n *\n * POST /wallet/transfers\n *\n * https://www.gate.io/docs/developers/apiv4/zh_CN/#%E4%BA%A4%E6%98%93%E8%B4%A6%E6%88%B7%E4%BA%92%E8%BD%AC\n */\nexport const postWalletTransfer = (\n credential: ICredential,\n params: {\n currency: string;\n from: string;\n to: string;\n amount: string;\n currency_pair?: string;\n settle?: string;\n },\n): Promise<{\n tx_id: string;\n}> => callPrivate(credential, 'POST', '/wallet/transfers', params);\n\n/**\n * 获取统一账户最多可转出\n *\n * https://www.gate.io/docs/developers/apiv4/zh_CN/#%E6%9F%A5%E8%AF%A2%E7%BB%9F%E4%B8%80%E8%B4%A6%E6%88%B7%E6%9C%80%E5%A4%9A%E5%8F%AF%E8%BD%AC%E5%87%BA\n */\nexport const getUnifiedTransferable = (\n credential: ICredential,\n params: {\n currency: string;\n },\n): Promise<{\n currency: string;\n amount: string;\n}> => callPrivate(credential, 'GET', `/unified/transferable`, params);\n\n/**\n * 查询合约账户变更历史\n *\n * https://www.gate.com/docs/developers/apiv4/zh_CN/#%E6%9F%A5%E8%AF%A2%E5%90%88%E7%BA%A6%E8%B4%A6%E6%88%B7%E5%8F%98%E6%9B%B4%E5%8E%86%E5%8F%B2\n */\nexport const getFutureAccountsBook = (\n credential: ICredential,\n params: {\n settle: string;\n contract?: string;\n limit?: number;\n offset?: number;\n from?: number;\n to?: number;\n type?: string; // | 'dnw' | 'pnl' | 'fee' | 'refr' | 'fund' | 'point_dnw' | 'point_fee' | 'point_refr' | 'bonus_offset'\n },\n): Promise<\n {\n time: number;\n change: string;\n balance: string;\n text: string;\n type: string;\n contract: string;\n trade_id: string;\n id: string;\n }[]\n> => callPrivate(credential, 'GET', `/futures/${params.settle}/account_book`, params);\n\n/**\n * 查询个人成交记录(时间区间)\n *\n * https://www.gate.com/docs/developers/apiv4/zh_CN/#%E6%9F%A5%E8%AF%A2%E4%B8%AA%E4%BA%BA%E6%88%90%E4%BA%A4%E8%AE%B0%E5%BD%95-%E6%97%B6%E9%97%B4%E5%8C%BA%E9%97%B4\n */\nexport const getFutureAccountsTrades = (\n credential: ICredential,\n params: {\n settle: string;\n contract?: string;\n limit?: number;\n offset?: number;\n from?: number;\n to?: number;\n role?: string;\n },\n): Promise<\n {\n trade_id: string;\n create_time: number;\n contract: string;\n order_id: string;\n size: string;\n price: string;\n text: string;\n fee: string;\n point_fee: string;\n role: string;\n close_size: string;\n }[]\n> => callPrivate(credential, 'GET', `/futures/${params.settle}/my_trades_timerange`, params);\n"]}
1
+ {"version":3,"file":"private-api.js","sourceRoot":"","sources":["../../src/api/private-api.ts"],"names":[],"mappings":"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","sourcesContent":["import type { GateParams, HttpMethod, IGateCredential } from './http-client';\nimport { requestPrivate } from './http-client';\n\nexport type ICredential = IGateCredential;\n\nconst callPrivate = <TResponse>(\n credential: ICredential,\n method: HttpMethod,\n path: string,\n params?: GateParams,\n) => requestPrivate<TResponse>(credential, method, path, params);\n\n/**\n * 获取用户账户信息\n *\n * https://www.gate.io/docs/developers/apiv4/zh_CN/#%E8%8E%B7%E5%8F%96%E7%94%A8%E6%88%B7%E8%B4%A6%E6%88%B7%E4%BF%A1%E6%81%AF\n */\nexport const getAccountDetail = (\n credential: ICredential,\n): Promise<{\n user_id: number;\n ip_whitelist: string[];\n currency_pairs: string[];\n key: {\n mode: number;\n };\n tier: number;\n}> => callPrivate(credential, 'GET', '/account/detail');\n\n/**\n * 获取统一账户信息\n *\n * https://www.gate.com/docs/developers/apiv4/zh_CN/#%E8%8E%B7%E5%8F%96%E7%BB%9F%E4%B8%80%E8%B4%A6%E6%88%B7%E4%BF%A1%E6%81%AF\n */\nexport const getUnifiedAccounts = (\n credential: ICredential,\n params?: { currency?: string },\n): Promise<{\n user_id: string;\n refresh_time: number;\n locket: boolean;\n balances: Record<\n string,\n {\n available: string;\n freeze: string;\n borrowed: string;\n negative_liab: string;\n futures_pos_liab: string;\n equity: string;\n total_freeze: string;\n total_liab: string;\n spot_in_use: string;\n cross_balance: string;\n iso_balance: string;\n funding: string;\n }\n >;\n total: string;\n borrowed: string;\n total_initial_margin: string;\n total_margin_balance: string;\n total_maintenance_margin: string;\n total_initial_margin_rate: string;\n total_maintenance_margin_rate: string;\n total_avail_margin: string;\n unified_account_total: string;\n unified_account_total_liab: string;\n unified_account_total_equity: string;\n leverage: string;\n spot_order_loss: string;\n spot_hedge: boolean;\n}> => callPrivate(credential, 'GET', '/unified/accounts', params);\n\n/**\n * 获取用户理财余额(Gate.io EarnUni 余币宝理财)\n *\n * https://www.gate.com/docs/developers/apiv4/zh_CN/#查询用户币种理财列表\n * 端点:GET /earn/uni/lends(需要鉴权)\n */\nexport const getEarnBalance = (\n credential: ICredential,\n params?: { currency?: string; page?: number; limit?: number },\n): Promise<\n Array<{\n currency: string;\n amount: string;\n lent_amount: string;\n frozen_amount: string;\n current_amount: string;\n min_rate?: string;\n interest_status?: string;\n reinvest_left_amount?: string;\n create_time?: number;\n update_time?: number;\n }>\n> => {\n // 输入参数校验\n const validatedParams = { ...params };\n if (validatedParams.currency !== undefined) {\n if (typeof validatedParams.currency !== 'string' || !/^[A-Za-z0-9]+$/.test(validatedParams.currency)) {\n throw new Error(`Invalid currency format: ${validatedParams.currency}`);\n }\n }\n if (validatedParams.page !== undefined) {\n if (!Number.isInteger(validatedParams.page) || validatedParams.page < 1) {\n throw new Error(`Invalid page: must be positive integer, got ${validatedParams.page}`);\n }\n }\n if (validatedParams.limit !== undefined) {\n if (\n !Number.isInteger(validatedParams.limit) ||\n validatedParams.limit < 1 ||\n validatedParams.limit > 1000\n ) {\n throw new Error(`Invalid limit: must be between 1 and 1000, got ${validatedParams.limit}`);\n }\n }\n\n return callPrivate(credential, 'GET', '/earn/uni/lends', validatedParams);\n};\n\n/**\n * 获取用户仓位列表\n *\n * https://www.gate.io/docs/developers/apiv4/zh_CN/#%E8%8E%B7%E5%8F%96%E7%94%A8%E6%88%B7%E4%BB%93%E4%BD%8D%E5%88%97%E8%A1%A8\n */\nexport const getFuturePositions = (\n credential: ICredential,\n quote_currency: string,\n params?: { holding?: boolean; limit?: number; offset?: number },\n): Promise<\n {\n user: number;\n contract: string;\n size: number;\n leverage: string;\n risk_limit: string;\n leverage_max: string;\n maintenance_rate: string;\n value: string;\n margin: string;\n entry_price: string;\n mark_price: string;\n unrealised_pnl: string;\n realised_pnl: string;\n mode: string;\n liq_price: string;\n }[]\n> => callPrivate(credential, 'GET', `/futures/${quote_currency}/positions`, params);\n\n/**\n * 查询合约订单列表\n *\n * https://www.gate.io/docs/developers/apiv4/zh_CN/#%E6%9F%A5%E8%AF%A2%E5%90%88%E7%BA%A6%E8%AE%A2%E5%8D%95%E5%88%97%E8%A1%A8\n */\nexport const getFuturesOrders = (\n credential: ICredential,\n settle: string,\n params: {\n contract?: string;\n status: string;\n limit?: number;\n offset?: number;\n last_id?: number;\n },\n): Promise<\n {\n id: string;\n contract: string;\n create_time: number;\n size: number;\n price: string;\n is_close: boolean;\n fill_price: string;\n left?: number;\n status?: string;\n text: string;\n }[]\n> => callPrivate(credential, 'GET', `/futures/${settle}/orders`, params);\n\n/**\n * 获取合约账号\n *\n * https://www.gate.io/docs/developers/apiv4/zh_CN/#%E8%8E%B7%E5%8F%96%E5%90%88%E7%BA%A6%E8%B4%A6%E5%8F%B7\n */\nexport const getFuturesAccounts = (\n credential: ICredential,\n settle: string,\n): Promise<{\n user: number;\n currency: string;\n total: string;\n unrealised_pnl: string;\n position_margin: string;\n order_margin: string;\n available: string;\n point: string;\n bonus: string;\n in_dual_mode: boolean;\n enable_evolved_classic: boolean;\n history: {\n dnw: string;\n pnl: string;\n fee: string;\n refr: string;\n fund: string;\n point_dnw: string;\n point_fee: string;\n point_refr: string;\n bonus_dnw: string;\n bonus_offset: string;\n };\n}> => callPrivate(credential, 'GET', `/futures/${settle}/accounts`);\n\n/**\n * 合约交易下单\n *\n * 下单时指定的是合约张数 size ,而非币的数量,每一张合约对应的币的数量是合约详情接口里返回的 quanto_multiplier\n *\n * 0 成交的订单在撤单 10 分钟之后无法再获取到,会提到订单不存在\n *\n * 设置 reduce_only 为 true 可以防止在减仓的时候穿仓\n *\n * 单仓模式下,如果需要平仓,需要设置 size 为 0 ,close 为 true\n *\n * 双仓模式下,平仓需要使用 auto_size 来设置平仓方向,并同时设置 reduce_only 为 true,size 为 0\n *\n * 设置 stp_act 决定使用限制用户自成交的策略,详细用法参考body参数stp_act\n *\n * https://www.gate.io/docs/developers/apiv4/zh_CN/#%E5%90%88%E7%BA%A6%E4%BA%A4%E6%98%93%E4%B8%8B%E5%8D%95\n */\nexport const postFutureOrders = (\n credential: ICredential,\n settle: string,\n params: {\n contract: string;\n size: number;\n iceberg?: number;\n price?: string;\n close?: boolean;\n reduce_only?: boolean;\n tif?: string;\n text?: string;\n auto_size?: string;\n stp_act?: string;\n },\n): Promise<{\n label?: string;\n message?: string;\n detail?: string;\n id: number;\n user: number;\n contract: string;\n create_time: number;\n size: number;\n iceberg: number;\n left: number;\n price: string;\n fill_price: string;\n mkfr: string;\n tkfr: string;\n tif: string;\n refu: number;\n is_reduce_only: boolean;\n is_close: boolean;\n is_liq: boolean;\n text: string;\n status: string;\n finish_time: number;\n finish_as: string;\n stp_id: number;\n stp_act: string;\n amend_text: string;\n}> => callPrivate(credential, 'POST', `/futures/${settle}/orders`, params);\n\n/**\n * 撤销单个订单\n *\n * https://www.gate.io/docs/developers/apiv4/zh_CN/#%E6%92%A4%E9%94%80%E5%8D%95%E4%B8%AA%E8%AE%A2%E5%8D%95-2\n */\nexport const deleteFutureOrders = (credential: ICredential, settle: string, order_id: string): Promise<{}> =>\n callPrivate(credential, 'DELETE', `/futures/${settle}/orders/${order_id}`);\n\n/**\n * 提现\n *\n * POST /withdrawals\n *\n * https://www.gate.io/docs/developers/apiv4/zh_CN/#%E6%8F%90%E7%8E%B0\n */\nexport const postWithdrawals = (\n credential: ICredential,\n params: {\n withdraw_order_id?: string;\n amount: string;\n currency: string;\n address?: string;\n memo?: string;\n chain: string;\n },\n): Promise<{\n id: string;\n txid: string;\n withdraw_order_id: string;\n timestamp: number;\n amount: string;\n currency: string;\n address: string;\n memo: string;\n status: string;\n chain: string;\n}> => callPrivate(credential, 'POST', '/withdrawals', params);\n\n/**\n * 获取币种充值地址\n *\n * https://www.gate.io/docs/developers/apiv4/zh_CN/#%E8%8E%B7%E5%8F%96%E5%B8%81%E7%A7%8D%E5%85%85%E5%80%BC%E5%9C%B0%E5%9D%80\n */\nexport const getDepositAddress = (\n credential: ICredential,\n params: {\n currency: string;\n },\n): Promise<{\n currency: string;\n address: string;\n multichain_addresses: {\n chain: string;\n address: string;\n payment_id: string;\n payment_name: string;\n obtain_failed: boolean;\n }[];\n}> => callPrivate(credential, 'GET', '/wallet/deposit_address', params);\n\n/**\n * 创建新的子账户\n *\n * https://www.gate.io/docs/developers/apiv4/zh_CN/#%E5%88%9B%E5%BB%BA%E6%96%B0%E7%9A%84%E5%AD%90%E8%B4%A6%E6%88%B7\n */\nexport const getSubAccountList = (\n credential: ICredential,\n params?: {\n type?: string;\n },\n): Promise<\n {\n remark: string;\n login_name: string;\n password: string;\n email: string;\n state: number;\n type: number;\n user_id: number;\n create_time: number;\n }[]\n> => callPrivate(credential, 'GET', '/sub_accounts', params);\n\n/**\n * 获取充值记录\n *\n * 记录查询时间范围不允许超过 30 天\n *\n * https://www.gate.io/docs/developers/apiv4/zh_CN/#%E8%8E%B7%E5%8F%96%E5%85%85%E5%80%BC%E8%AE%B0%E5%BD%95\n */\nexport const getDepositHistory = (\n credential: ICredential,\n params?: {\n currency?: string;\n from?: number;\n to?: number;\n limit?: number;\n offset?: number;\n },\n): Promise<\n {\n id: string;\n txid: string;\n withdraw_order_id: string;\n timestamp: number;\n amount: string;\n currency: string;\n address: string;\n memo: string;\n status: string;\n chain: string;\n }[]\n> => callPrivate(credential, 'GET', '/wallet/deposits', params);\n\n/**\n * 获取提现记录\n *\n * 记录查询时间范围不允许超过 30 天\n *\n * https://www.gate.io/docs/developers/apiv4/zh_CN/#%E8%8E%B7%E5%8F%96%E5%B8%81%E7%A7%8D%E5%85%85%E5%80%BC%E5%9C%B0%E5%9D%80\n */\nexport const getWithdrawalHistory = (\n credential: ICredential,\n params?: {\n currency?: string;\n from?: number;\n to?: number;\n limit?: number;\n offset?: number;\n },\n): Promise<\n {\n id: string;\n txid: string;\n withdraw_order_id: string;\n timestamp: number;\n amount: string;\n currency: string;\n address: string;\n memo: string;\n status: string;\n chain: string;\n }[]\n> => callPrivate(credential, 'GET', '/wallet/withdrawals', params);\n\n/**\n * 获取现货交易账户列表\n *\n * https://www.gate.io/docs/developers/apiv4/zh_CN/#%E8%8E%B7%E5%8F%96%E7%8E%B0%E8%B4%A7%E4%BA%A4%E6%98%93%E8%B4%A6%E6%88%B7%E5%88%97%E8%A1%A8\n */\nexport const getSpotAccounts = (\n credential: ICredential,\n params?: {\n currency?: string;\n },\n): Promise<\n {\n currency: string;\n available: string;\n locked: string;\n update_id: string;\n }[]\n> => callPrivate(credential, 'GET', '/spot/accounts', params);\n\n/**\n * 交易账户互转\n *\n * POST /wallet/transfers\n *\n * https://www.gate.io/docs/developers/apiv4/zh_CN/#%E4%BA%A4%E6%98%93%E8%B4%A6%E6%88%B7%E4%BA%92%E8%BD%AC\n */\nexport const postWalletTransfer = (\n credential: ICredential,\n params: {\n currency: string;\n from: string;\n to: string;\n amount: string;\n currency_pair?: string;\n settle?: string;\n },\n): Promise<{\n tx_id: string;\n}> => callPrivate(credential, 'POST', '/wallet/transfers', params);\n\n/**\n * 获取统一账户最多可转出\n *\n * https://www.gate.io/docs/developers/apiv4/zh_CN/#%E6%9F%A5%E8%AF%A2%E7%BB%9F%E4%B8%80%E8%B4%A6%E6%88%B7%E6%9C%80%E5%A4%9A%E5%8F%AF%E8%BD%AC%E5%87%BA\n */\nexport const getUnifiedTransferable = (\n credential: ICredential,\n params: {\n currency: string;\n },\n): Promise<{\n currency: string;\n amount: string;\n}> => callPrivate(credential, 'GET', `/unified/transferable`, params);\n\n/**\n * 查询合约账户变更历史\n *\n * https://www.gate.com/docs/developers/apiv4/zh_CN/#%E6%9F%A5%E8%AF%A2%E5%90%88%E7%BA%A6%E8%B4%A6%E6%88%B7%E5%8F%98%E6%9B%B4%E5%8E%86%E5%8F%B2\n */\nexport const getFutureAccountsBook = (\n credential: ICredential,\n params: {\n settle: string;\n contract?: string;\n limit?: number;\n offset?: number;\n from?: number;\n to?: number;\n type?: string; // | 'dnw' | 'pnl' | 'fee' | 'refr' | 'fund' | 'point_dnw' | 'point_fee' | 'point_refr' | 'bonus_offset'\n },\n): Promise<\n {\n time: number;\n change: string;\n balance: string;\n text: string;\n type: string;\n contract: string;\n trade_id: string;\n id: string;\n }[]\n> => callPrivate(credential, 'GET', `/futures/${params.settle}/account_book`, params);\n\n/**\n * 查询个人成交记录(时间区间)\n *\n * https://www.gate.com/docs/developers/apiv4/zh_CN/#%E6%9F%A5%E8%AF%A2%E4%B8%AA%E4%BA%BA%E6%88%90%E4%BA%A4%E8%AE%B0%E5%BD%95-%E6%97%B6%E9%97%B4%E5%8C%BA%E9%97%B4\n */\nexport const getFutureAccountsTrades = (\n credential: ICredential,\n params: {\n settle: string;\n contract?: string;\n limit?: number;\n offset?: number;\n from?: number;\n to?: number;\n role?: string;\n },\n): Promise<\n {\n trade_id: string;\n create_time: number;\n contract: string;\n order_id: string;\n size: string;\n price: string;\n text: string;\n fee: string;\n point_fee: string;\n role: string;\n close_size: string;\n }[]\n> => callPrivate(credential, 'GET', `/futures/${params.settle}/my_trades_timerange`, params);\n"]}
@@ -46,6 +46,26 @@ export const getFuturesTickers = (settle, params) => requestPublic('GET', `/futu
46
46
  * https://www.gate.com/docs/developers/apiv4/zh_CN/#%E8%8E%B7%E5%8F%96%E4%BA%A4%E6%98%93%E5%AF%B9-ticker-%E4%BF%A1%E6%81%AF
47
47
  */
48
48
  export const getSpotTickers = (params) => requestPublic('GET', `/spot/tickers`, params);
49
+ /**
50
+ * 获取指定币种对 USDT 的现货价格
51
+ *
52
+ * @param currency 币种代码(如 BTC、ETH)
53
+ * @returns 该币种对 USDT 的最新价格,如果找不到则返回 1
54
+ */
55
+ export const getSpotPrice = async (currency) => {
56
+ const tickers = await getSpotTickers({});
57
+ // 特殊币种映射(参考 unified.ts 和 earning.ts)
58
+ let currencyPair = `${currency}_USDT`;
59
+ if (currency === 'SOL2' || currency === 'GTSOL') {
60
+ currencyPair = 'SOL_USDT';
61
+ }
62
+ const ticker = tickers.find((t) => t.currency_pair === currencyPair);
63
+ if (!ticker) {
64
+ console.warn(`No spot ticker found for ${currencyPair}, using 1 as default`);
65
+ return 1;
66
+ }
67
+ return Number(ticker.last);
68
+ };
49
69
  /**
50
70
  * 查询支持的所有现货交易对
51
71
  * https://www.gate.com/docs/developers/apiv4/zh_CN/#%E6%9F%A5%E8%AF%A2%E6%94%AF%E6%8C%81%E7%9A%84%E6%89%80%E6%9C%89%E4%BA%A4%E6%98%93%E5%AF%B9
@@ -1 +1 @@
1
- {"version":3,"file":"public-api.js","sourceRoot":"","sources":["../../src/api/public-api.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAE7C;;;;GAIG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,CACjC,MAAc,EACd,MAA4C,EA8C5C,EAAE,CAAC,aAAa,CAAC,KAAK,EAAE,YAAY,MAAM,YAAY,EAAE,MAAM,CAAC,CAAC;AAElE;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAClC,MAAc,EACd,MAAyF,EAMzF,EAAE,CAAC,aAAa,CAAC,KAAK,EAAE,YAAY,MAAM,eAAe,EAAE,MAAM,CAAC,CAAC;AAErE;;;;GAIG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG,CACpC,MAAc,EACd,MAMC,EAgBD,EAAE,CAAC,aAAa,CAAC,KAAK,EAAE,YAAY,MAAM,eAAe,EAAE,MAAM,CAAC,CAAC;AAErE;;;;GAIG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,MAMnC,EAQC,EAAE,CAAC,aAAa,CAAC,KAAK,EAAE,oBAAoB,EAAE,MAAM,CAAC,CAAC;AAExD;;;;GAIG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,KAAK,EACtC,MAAc,EACd,MAKC,EAaA,EAAE,CACH,WAAW,CAAC,QAAQ,CAAC,sBAAsB,MAAM,EAAE,EAAE,GAAG,EAAE,KAAM,EAAE,GAAG,EAAE,CACrE,aAAa,CAAC,KAAK,EAAE,YAAY,MAAM,aAAa,EAAE,MAAM,CAAC,CAC9D,CAAC;AAEJ;;;;GAIG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAC/B,MAAc,EACd,MAA8B,EAyB9B,EAAE,CAAC,aAAa,CAAC,KAAK,EAAE,YAAY,MAAM,UAAU,EAAE,MAAM,CAAC,CAAC;AAEhE;;;;GAIG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,MAG9B,EAoBC,EAAE,CAAC,aAAa,CAAC,KAAK,EAAE,eAAe,EAAE,MAAM,CAAC,CAAC;AAEnD;;;GAGG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,GAqBlC,EAAE,CAAC,aAAa,CAAC,KAAK,EAAE,sBAAsB,CAAC,CAAC","sourcesContent":["import { requestPublic } from './http-client';\nimport { rateLimiter } from './rate-limiter';\n\n/**\n * 查询所有的合约信息\n *\n * https://www.gate.io/docs/developers/apiv4/zh_CN/#%E6%9F%A5%E8%AF%A2%E6%89%80%E6%9C%89%E7%9A%84%E5%90%88%E7%BA%A6%E4%BF%A1%E6%81%AF\n */\nexport const getFuturesContracts = (\n settle: string,\n params?: { limit?: number; offset?: number },\n): Promise<\n {\n name: string;\n type: string;\n quanto_multiplier: string;\n ref_discount_rate: string;\n order_price_deviate: string;\n maintenance_rate: string;\n mark_type: string;\n last_price: string;\n mark_price: string;\n index_price: string;\n funding_rate_indicative: string;\n mark_price_round: string;\n funding_offset: number;\n in_delisting: boolean;\n risk_limit_base: string;\n interest_rate: string;\n order_price_round: string;\n order_size_min: number;\n ref_rebate_rate: string;\n funding_interval: number;\n risk_limit_step: string;\n leverage_min: string;\n leverage_max: string;\n risk_limit_max: string;\n maker_fee_rate: string;\n taker_fee_rate: string;\n funding_rate: string;\n order_size_max: number;\n funding_next_apply: number;\n short_users: number;\n config_change_time: number;\n trade_size: number;\n position_size: number;\n long_users: number;\n funding_impact_value: string;\n orders_limit: number;\n trade_id: number;\n orderbook_id: number;\n enable_bonus: boolean;\n enable_credit: boolean;\n create_time: number;\n funding_cap_ratio: string;\n }[]\n> => requestPublic('GET', `/futures/${settle}/contracts`, params);\n\n/**\n * 合约市场历史资金费率\n *\n * - Note: 该接口返回的数据是按照时间倒序排列的\n * - Note: limit 参数最大值为 1000\n * - Note: t 字段为秒级时间戳 (Unix Second),r 字段为资金费率 (0-1 单位)\n *\n * https://www.gate.io/docs/developers/apiv4/zh_CN/#%E5%90%88%E7%BA%A6%E5%B8%82%E5%9C%BA%E5%8E%86%E5%8F%B2%E8%B5%84%E9%87%91%E8%B4%B9%E7%8E%87\n */\nexport const getFutureFundingRate = (\n settle: string,\n params: { contract: string; limit?: number; from?: number; to?: number; offset?: number },\n): Promise<\n {\n t: number;\n r: string;\n }[]\n> => requestPublic('GET', `/futures/${settle}/funding_rate`, params);\n\n/**\n * 合约市场 K 线图\n *\n * https://www.gate.com/docs/developers/apiv4/zh_CN/#%E5%90%88%E7%BA%A6%E5%B8%82%E5%9C%BA-k-%E7%BA%BF%E5%9B%BE\n */\nexport const getFuturesCandlesticks = (\n settle: string,\n params: {\n contract: string;\n interval: string;\n from?: number;\n to?: number;\n limit?: number;\n },\n): Promise<\n Array<{\n /** Unix second */\n t: number;\n /** Open */\n o: string;\n /** High */\n h: string;\n /** Low */\n l: string;\n /** Close */\n c: string;\n /** Volume */\n v: string;\n }>\n> => requestPublic('GET', `/futures/${settle}/candlesticks`, params);\n\n/**\n * 市场 K 线图\n *\n * https://www.gate.com/docs/developers/apiv4/zh_CN/#%E5%B8%82%E5%9C%BA-k-%E7%BA%BF%E5%9B%BE\n */\nexport const getSpotCandlesticks = (params: {\n currency_pair: string;\n interval: string;\n from?: number;\n to?: number;\n limit?: number;\n}): Promise<\n Array<\n /**\n * [t, v, c, h, l, o]\n * - t: Unix second\n */\n [string, string, string, string, string, string]\n >\n> => requestPublic('GET', `/spot/candlesticks`, params);\n\n/**\n * 查询合约市场深度信息\n *\n * https://www.gate.io/docs/developers/apiv4/zh_CN/#%E6%9F%A5%E8%AF%A2%E5%90%88%E7%BA%A6%E5%B8%82%E5%9C%BA%E6%B7%B1%E5%BA%A6%E4%BF%A1%E6%81%AF\n */\nexport const getFuturesOrderBook = async (\n settle: string,\n params: {\n contract: string;\n interval?: string;\n limit?: number;\n with_id?: boolean;\n },\n): Promise<{\n id: number;\n current: number;\n update: number;\n asks: {\n p: string;\n s: string;\n }[];\n bids: {\n p: string;\n s: string;\n }[];\n}> =>\n rateLimiter.schedule(`futures-order-book:${settle}`, 200, 10_000, () =>\n requestPublic('GET', `/futures/${settle}/order_book`, params),\n );\n\n/**\n * 获取所有合约交易行情统计\n *\n * https://www.gate.io/docs/developers/apiv4/zh_CN/#%E8%8E%B7%E5%8F%96%E6%89%80%E6%9C%89%E5%90%88%E7%BA%A6%E4%BA%A4%E6%98%93%E8%A1%8C%E6%83%85%E7%BB%9F%E8%AE%A1\n */\nexport const getFuturesTickers = (\n settle: string,\n params?: { contract?: string },\n): Promise<\n {\n contract: string;\n last: string;\n change_percentage: string;\n total_size: string;\n low_24h: string;\n high_24h: string;\n volume_24h: string;\n volume_24h_btc: string;\n volume_24h_usd: string;\n volume_24h_base: string;\n volume_24h_quote: string;\n volume_24h_settle: string;\n mark_price: string;\n funding_rate: string;\n funding_rate_indicative: string;\n index_price: string;\n quanto_base_rate: string;\n basis_rate: string;\n basis_value: string;\n lowest_ask: string;\n highest_bid: string;\n }[]\n> => requestPublic('GET', `/futures/${settle}/tickers`, params);\n\n/**\n * 获取交易对 ticker 信息\n *\n * https://www.gate.com/docs/developers/apiv4/zh_CN/#%E8%8E%B7%E5%8F%96%E4%BA%A4%E6%98%93%E5%AF%B9-ticker-%E4%BF%A1%E6%81%AF\n */\nexport const getSpotTickers = (params: {\n currency_pair?: string;\n timezone?: string;\n}): Promise<\n Array<{\n currency_pair: string;\n last: string;\n lowest_ask: string;\n lowest_size: string;\n highest_bid: string;\n highest_size: string;\n change_percentage: string;\n change_utc0: string;\n change_utc8: string;\n base_volume: string;\n quote_volume: string;\n high_24h: string;\n low_24h: string;\n etf_net_value: string;\n etf_pre_net_value: string;\n etf_pre_timestamp: string;\n etf_leverage: string;\n }>\n> => requestPublic('GET', `/spot/tickers`, params);\n\n/**\n * 查询支持的所有现货交易对\n * https://www.gate.com/docs/developers/apiv4/zh_CN/#%E6%9F%A5%E8%AF%A2%E6%94%AF%E6%8C%81%E7%9A%84%E6%89%80%E6%9C%89%E4%BA%A4%E6%98%93%E5%AF%B9\n */\nexport const getSpotCurrencyPairs = (): Promise<\n Array<{\n id: string;\n base: string;\n base_name: string;\n quote: string;\n quote_name: string;\n fee: string;\n min_base_amount: string;\n min_quote_amount: string;\n max_base_amount: string;\n max_quote_amount: string;\n amount_precision: Number;\n precision: Number;\n trade_status: string;\n sell_start: Number;\n buy_start: number;\n delisting_time: number;\n trade_url: string;\n st_tag: boolean;\n }>\n> => requestPublic('GET', `/spot/currency_pairs`);\n"]}
1
+ {"version":3,"file":"public-api.js","sourceRoot":"","sources":["../../src/api/public-api.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAE7C;;;;GAIG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,CACjC,MAAc,EACd,MAA4C,EA8C5C,EAAE,CAAC,aAAa,CAAC,KAAK,EAAE,YAAY,MAAM,YAAY,EAAE,MAAM,CAAC,CAAC;AAElE;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAClC,MAAc,EACd,MAAyF,EAMzF,EAAE,CAAC,aAAa,CAAC,KAAK,EAAE,YAAY,MAAM,eAAe,EAAE,MAAM,CAAC,CAAC;AAErE;;;;GAIG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG,CACpC,MAAc,EACd,MAMC,EAgBD,EAAE,CAAC,aAAa,CAAC,KAAK,EAAE,YAAY,MAAM,eAAe,EAAE,MAAM,CAAC,CAAC;AAErE;;;;GAIG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,MAMnC,EAQC,EAAE,CAAC,aAAa,CAAC,KAAK,EAAE,oBAAoB,EAAE,MAAM,CAAC,CAAC;AAExD;;;;GAIG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,KAAK,EACtC,MAAc,EACd,MAKC,EAaA,EAAE,CACH,WAAW,CAAC,QAAQ,CAAC,sBAAsB,MAAM,EAAE,EAAE,GAAG,EAAE,KAAM,EAAE,GAAG,EAAE,CACrE,aAAa,CAAC,KAAK,EAAE,YAAY,MAAM,aAAa,EAAE,MAAM,CAAC,CAC9D,CAAC;AAEJ;;;;GAIG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAC/B,MAAc,EACd,MAA8B,EAyB9B,EAAE,CAAC,aAAa,CAAC,KAAK,EAAE,YAAY,MAAM,UAAU,EAAE,MAAM,CAAC,CAAC;AAEhE;;;;GAIG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,MAG9B,EAoBC,EAAE,CAAC,aAAa,CAAC,KAAK,EAAE,eAAe,EAAE,MAAM,CAAC,CAAC;AAEnD;;;;;GAKG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,KAAK,EAAE,QAAgB,EAAmB,EAAE;IACtE,MAAM,OAAO,GAAG,MAAM,cAAc,CAAC,EAAE,CAAC,CAAC;IACzC,qCAAqC;IACrC,IAAI,YAAY,GAAG,GAAG,QAAQ,OAAO,CAAC;IACtC,IAAI,QAAQ,KAAK,MAAM,IAAI,QAAQ,KAAK,OAAO,EAAE,CAAC;QAChD,YAAY,GAAG,UAAU,CAAC;IAC5B,CAAC;IACD,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,aAAa,KAAK,YAAY,CAAC,CAAC;IACrE,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,OAAO,CAAC,IAAI,CAAC,4BAA4B,YAAY,sBAAsB,CAAC,CAAC;QAC7E,OAAO,CAAC,CAAC;IACX,CAAC;IACD,OAAO,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;AAC7B,CAAC,CAAC;AAEF;;;GAGG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,GAqBlC,EAAE,CAAC,aAAa,CAAC,KAAK,EAAE,sBAAsB,CAAC,CAAC","sourcesContent":["import { requestPublic } from './http-client';\nimport { rateLimiter } from './rate-limiter';\n\n/**\n * 查询所有的合约信息\n *\n * https://www.gate.io/docs/developers/apiv4/zh_CN/#%E6%9F%A5%E8%AF%A2%E6%89%80%E6%9C%89%E7%9A%84%E5%90%88%E7%BA%A6%E4%BF%A1%E6%81%AF\n */\nexport const getFuturesContracts = (\n settle: string,\n params?: { limit?: number; offset?: number },\n): Promise<\n {\n name: string;\n type: string;\n quanto_multiplier: string;\n ref_discount_rate: string;\n order_price_deviate: string;\n maintenance_rate: string;\n mark_type: string;\n last_price: string;\n mark_price: string;\n index_price: string;\n funding_rate_indicative: string;\n mark_price_round: string;\n funding_offset: number;\n in_delisting: boolean;\n risk_limit_base: string;\n interest_rate: string;\n order_price_round: string;\n order_size_min: number;\n ref_rebate_rate: string;\n funding_interval: number;\n risk_limit_step: string;\n leverage_min: string;\n leverage_max: string;\n risk_limit_max: string;\n maker_fee_rate: string;\n taker_fee_rate: string;\n funding_rate: string;\n order_size_max: number;\n funding_next_apply: number;\n short_users: number;\n config_change_time: number;\n trade_size: number;\n position_size: number;\n long_users: number;\n funding_impact_value: string;\n orders_limit: number;\n trade_id: number;\n orderbook_id: number;\n enable_bonus: boolean;\n enable_credit: boolean;\n create_time: number;\n funding_cap_ratio: string;\n }[]\n> => requestPublic('GET', `/futures/${settle}/contracts`, params);\n\n/**\n * 合约市场历史资金费率\n *\n * - Note: 该接口返回的数据是按照时间倒序排列的\n * - Note: limit 参数最大值为 1000\n * - Note: t 字段为秒级时间戳 (Unix Second),r 字段为资金费率 (0-1 单位)\n *\n * https://www.gate.io/docs/developers/apiv4/zh_CN/#%E5%90%88%E7%BA%A6%E5%B8%82%E5%9C%BA%E5%8E%86%E5%8F%B2%E8%B5%84%E9%87%91%E8%B4%B9%E7%8E%87\n */\nexport const getFutureFundingRate = (\n settle: string,\n params: { contract: string; limit?: number; from?: number; to?: number; offset?: number },\n): Promise<\n {\n t: number;\n r: string;\n }[]\n> => requestPublic('GET', `/futures/${settle}/funding_rate`, params);\n\n/**\n * 合约市场 K 线图\n *\n * https://www.gate.com/docs/developers/apiv4/zh_CN/#%E5%90%88%E7%BA%A6%E5%B8%82%E5%9C%BA-k-%E7%BA%BF%E5%9B%BE\n */\nexport const getFuturesCandlesticks = (\n settle: string,\n params: {\n contract: string;\n interval: string;\n from?: number;\n to?: number;\n limit?: number;\n },\n): Promise<\n Array<{\n /** Unix second */\n t: number;\n /** Open */\n o: string;\n /** High */\n h: string;\n /** Low */\n l: string;\n /** Close */\n c: string;\n /** Volume */\n v: string;\n }>\n> => requestPublic('GET', `/futures/${settle}/candlesticks`, params);\n\n/**\n * 市场 K 线图\n *\n * https://www.gate.com/docs/developers/apiv4/zh_CN/#%E5%B8%82%E5%9C%BA-k-%E7%BA%BF%E5%9B%BE\n */\nexport const getSpotCandlesticks = (params: {\n currency_pair: string;\n interval: string;\n from?: number;\n to?: number;\n limit?: number;\n}): Promise<\n Array<\n /**\n * [t, v, c, h, l, o]\n * - t: Unix second\n */\n [string, string, string, string, string, string]\n >\n> => requestPublic('GET', `/spot/candlesticks`, params);\n\n/**\n * 查询合约市场深度信息\n *\n * https://www.gate.io/docs/developers/apiv4/zh_CN/#%E6%9F%A5%E8%AF%A2%E5%90%88%E7%BA%A6%E5%B8%82%E5%9C%BA%E6%B7%B1%E5%BA%A6%E4%BF%A1%E6%81%AF\n */\nexport const getFuturesOrderBook = async (\n settle: string,\n params: {\n contract: string;\n interval?: string;\n limit?: number;\n with_id?: boolean;\n },\n): Promise<{\n id: number;\n current: number;\n update: number;\n asks: {\n p: string;\n s: string;\n }[];\n bids: {\n p: string;\n s: string;\n }[];\n}> =>\n rateLimiter.schedule(`futures-order-book:${settle}`, 200, 10_000, () =>\n requestPublic('GET', `/futures/${settle}/order_book`, params),\n );\n\n/**\n * 获取所有合约交易行情统计\n *\n * https://www.gate.io/docs/developers/apiv4/zh_CN/#%E8%8E%B7%E5%8F%96%E6%89%80%E6%9C%89%E5%90%88%E7%BA%A6%E4%BA%A4%E6%98%93%E8%A1%8C%E6%83%85%E7%BB%9F%E8%AE%A1\n */\nexport const getFuturesTickers = (\n settle: string,\n params?: { contract?: string },\n): Promise<\n {\n contract: string;\n last: string;\n change_percentage: string;\n total_size: string;\n low_24h: string;\n high_24h: string;\n volume_24h: string;\n volume_24h_btc: string;\n volume_24h_usd: string;\n volume_24h_base: string;\n volume_24h_quote: string;\n volume_24h_settle: string;\n mark_price: string;\n funding_rate: string;\n funding_rate_indicative: string;\n index_price: string;\n quanto_base_rate: string;\n basis_rate: string;\n basis_value: string;\n lowest_ask: string;\n highest_bid: string;\n }[]\n> => requestPublic('GET', `/futures/${settle}/tickers`, params);\n\n/**\n * 获取交易对 ticker 信息\n *\n * https://www.gate.com/docs/developers/apiv4/zh_CN/#%E8%8E%B7%E5%8F%96%E4%BA%A4%E6%98%93%E5%AF%B9-ticker-%E4%BF%A1%E6%81%AF\n */\nexport const getSpotTickers = (params: {\n currency_pair?: string;\n timezone?: string;\n}): Promise<\n Array<{\n currency_pair: string;\n last: string;\n lowest_ask: string;\n lowest_size: string;\n highest_bid: string;\n highest_size: string;\n change_percentage: string;\n change_utc0: string;\n change_utc8: string;\n base_volume: string;\n quote_volume: string;\n high_24h: string;\n low_24h: string;\n etf_net_value: string;\n etf_pre_net_value: string;\n etf_pre_timestamp: string;\n etf_leverage: string;\n }>\n> => requestPublic('GET', `/spot/tickers`, params);\n\n/**\n * 获取指定币种对 USDT 的现货价格\n *\n * @param currency 币种代码(如 BTC、ETH)\n * @returns 该币种对 USDT 的最新价格,如果找不到则返回 1\n */\nexport const getSpotPrice = async (currency: string): Promise<number> => {\n const tickers = await getSpotTickers({});\n // 特殊币种映射(参考 unified.ts 和 earning.ts)\n let currencyPair = `${currency}_USDT`;\n if (currency === 'SOL2' || currency === 'GTSOL') {\n currencyPair = 'SOL_USDT';\n }\n const ticker = tickers.find((t) => t.currency_pair === currencyPair);\n if (!ticker) {\n console.warn(`No spot ticker found for ${currencyPair}, using 1 as default`);\n return 1;\n }\n return Number(ticker.last);\n};\n\n/**\n * 查询支持的所有现货交易对\n * https://www.gate.com/docs/developers/apiv4/zh_CN/#%E6%9F%A5%E8%AF%A2%E6%94%AF%E6%8C%81%E7%9A%84%E6%89%80%E6%9C%89%E4%BA%A4%E6%98%93%E5%AF%B9\n */\nexport const getSpotCurrencyPairs = (): Promise<\n Array<{\n id: string;\n base: string;\n base_name: string;\n quote: string;\n quote_name: string;\n fee: string;\n min_base_amount: string;\n min_quote_amount: string;\n max_base_amount: string;\n max_quote_amount: string;\n amount_precision: Number;\n precision: Number;\n trade_status: string;\n sell_start: Number;\n buy_start: number;\n delisting_time: number;\n trade_url: string;\n st_tag: boolean;\n }>\n> => requestPublic('GET', `/spot/currency_pairs`);\n"]}
@@ -0,0 +1,61 @@
1
+ import { makeSpotPosition } from '@yuants/data-account';
2
+ import { encodePath } from '@yuants/utils';
3
+ import { getEarnBalance } from '../../api/private-api';
4
+ import { getSpotPrice } from '../../api/public-api';
5
+ /**
6
+ * 简单的并发限制器(支持带参数的函数)
7
+ */
8
+ const createConcurrencyLimiter = (concurrency, fn) => {
9
+ let running = 0;
10
+ const queue = [];
11
+ const runNext = () => {
12
+ if (running >= concurrency || queue.length === 0)
13
+ return;
14
+ running++;
15
+ const task = queue.shift();
16
+ task();
17
+ };
18
+ return (...args) => {
19
+ return new Promise((resolve, reject) => {
20
+ const task = () => {
21
+ fn(...args)
22
+ .then(resolve, reject)
23
+ .finally(() => {
24
+ running--;
25
+ runNext();
26
+ });
27
+ };
28
+ queue.push(task);
29
+ runNext();
30
+ });
31
+ };
32
+ };
33
+ // 限制现货价格查询的并发数(避免触发 API 限流)
34
+ const limitedGetSpotPrice = createConcurrencyLimiter(5, getSpotPrice);
35
+ /**
36
+ * 获取理财账户信息
37
+ */
38
+ export const getEarningAccountInfo = async (credential) => {
39
+ const balances = await getEarnBalance(credential, {});
40
+ const positions = await Promise.all(balances.map(async (balance) => {
41
+ // 过滤零余额条目
42
+ if (+balance.amount <= 0)
43
+ return undefined;
44
+ // 计算可用余额:理财总数量减去已申请赎回未到账的冻结部分
45
+ const frozen = +balance.frozen_amount || 0;
46
+ const freeVolume = Math.max(0, +balance.amount - frozen);
47
+ // 获取币种对 USDT 的现货价格(getSpotPrice 已处理特殊映射和默认值)
48
+ const closablePrice = await limitedGetSpotPrice(balance.currency);
49
+ return makeSpotPosition({
50
+ datasource_id: 'GATE',
51
+ position_id: `earning/${balance.currency}`,
52
+ product_id: encodePath('GATE', 'EARNING', balance.currency),
53
+ volume: +balance.amount,
54
+ free_volume: freeVolume,
55
+ closable_price: closablePrice,
56
+ });
57
+ }));
58
+ // 过滤 undefined 条目
59
+ return positions.filter((pos) => !!pos);
60
+ };
61
+ //# sourceMappingURL=earning.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"earning.js","sourceRoot":"","sources":["../../../src/services/accounts/earning.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAkB,MAAM,sBAAsB,CAAC;AACxE,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,EAAE,cAAc,EAAe,MAAM,uBAAuB,CAAC;AACpE,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAEpD;;GAEG;AACH,MAAM,wBAAwB,GAAG,CAC/B,WAAmB,EACnB,EAAiC,EACA,EAAE;IACnC,IAAI,OAAO,GAAG,CAAC,CAAC;IAChB,MAAM,KAAK,GAAsB,EAAE,CAAC;IAEpC,MAAM,OAAO,GAAG,GAAG,EAAE;QACnB,IAAI,OAAO,IAAI,WAAW,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO;QACzD,OAAO,EAAE,CAAC;QACV,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,EAAG,CAAC;QAC5B,IAAI,EAAE,CAAC;IACT,CAAC,CAAC;IAEF,OAAO,CAAC,GAAG,IAAU,EAAE,EAAE;QACvB,OAAO,IAAI,OAAO,CAAI,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACxC,MAAM,IAAI,GAAG,GAAG,EAAE;gBAChB,EAAE,CAAC,GAAG,IAAI,CAAC;qBACR,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC;qBACrB,OAAO,CAAC,GAAG,EAAE;oBACZ,OAAO,EAAE,CAAC;oBACV,OAAO,EAAE,CAAC;gBACZ,CAAC,CAAC,CAAC;YACP,CAAC,CAAC;YACF,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACjB,OAAO,EAAE,CAAC;QACZ,CAAC,CAAC,CAAC;IACL,CAAC,CAAC;AACJ,CAAC,CAAC;AAEF,4BAA4B;AAC5B,MAAM,mBAAmB,GAAG,wBAAwB,CAAC,CAAC,EAAE,YAAY,CAAC,CAAC;AAEtE;;GAEG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,KAAK,EAAE,UAAuB,EAAwB,EAAE;IAC3F,MAAM,QAAQ,GAAG,MAAM,cAAc,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;IAEtD,MAAM,SAAS,GAAG,MAAM,OAAO,CAAC,GAAG,CACjC,QAAQ,CAAC,GAAG,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE;QAC7B,UAAU;QACV,IAAI,CAAC,OAAO,CAAC,MAAM,IAAI,CAAC;YAAE,OAAO,SAAS,CAAC;QAE3C,8BAA8B;QAC9B,MAAM,MAAM,GAAG,CAAC,OAAO,CAAC,aAAa,IAAI,CAAC,CAAC;QAC3C,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,MAAM,GAAG,MAAM,CAAC,CAAC;QAEzD,6CAA6C;QAC7C,MAAM,aAAa,GAAG,MAAM,mBAAmB,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QAElE,OAAO,gBAAgB,CAAC;YACtB,aAAa,EAAE,MAAM;YACrB,WAAW,EAAE,WAAW,OAAO,CAAC,QAAQ,EAAE;YAC1C,UAAU,EAAE,UAAU,CAAC,MAAM,EAAE,SAAS,EAAE,OAAO,CAAC,QAAQ,CAAC;YAC3D,MAAM,EAAE,CAAC,OAAO,CAAC,MAAM;YACvB,WAAW,EAAE,UAAU;YACvB,cAAc,EAAE,aAAa;SAC9B,CAAC,CAAC;IACL,CAAC,CAAC,CACH,CAAC;IAEF,kBAAkB;IAClB,OAAO,SAAS,CAAC,MAAM,CAAC,CAAC,GAAG,EAAoB,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;AAC5D,CAAC,CAAC","sourcesContent":["import { makeSpotPosition, type IPosition } from '@yuants/data-account';\nimport { encodePath } from '@yuants/utils';\nimport { getEarnBalance, ICredential } from '../../api/private-api';\nimport { getSpotPrice } from '../../api/public-api';\n\n/**\n * 简单的并发限制器(支持带参数的函数)\n */\nconst createConcurrencyLimiter = <T, Args extends any[]>(\n concurrency: number,\n fn: (...args: Args) => Promise<T>,\n): ((...args: Args) => Promise<T>) => {\n let running = 0;\n const queue: Array<() => void> = [];\n\n const runNext = () => {\n if (running >= concurrency || queue.length === 0) return;\n running++;\n const task = queue.shift()!;\n task();\n };\n\n return (...args: Args) => {\n return new Promise<T>((resolve, reject) => {\n const task = () => {\n fn(...args)\n .then(resolve, reject)\n .finally(() => {\n running--;\n runNext();\n });\n };\n queue.push(task);\n runNext();\n });\n };\n};\n\n// 限制现货价格查询的并发数(避免触发 API 限流)\nconst limitedGetSpotPrice = createConcurrencyLimiter(5, getSpotPrice);\n\n/**\n * 获取理财账户信息\n */\nexport const getEarningAccountInfo = async (credential: ICredential): Promise<IPosition[]> => {\n const balances = await getEarnBalance(credential, {});\n\n const positions = await Promise.all(\n balances.map(async (balance) => {\n // 过滤零余额条目\n if (+balance.amount <= 0) return undefined;\n\n // 计算可用余额:理财总数量减去已申请赎回未到账的冻结部分\n const frozen = +balance.frozen_amount || 0;\n const freeVolume = Math.max(0, +balance.amount - frozen);\n\n // 获取币种对 USDT 的现货价格(getSpotPrice 已处理特殊映射和默认值)\n const closablePrice = await limitedGetSpotPrice(balance.currency);\n\n return makeSpotPosition({\n datasource_id: 'GATE',\n position_id: `earning/${balance.currency}`,\n product_id: encodePath('GATE', 'EARNING', balance.currency),\n volume: +balance.amount,\n free_volume: freeVolume,\n closable_price: closablePrice,\n });\n }),\n );\n\n // 过滤 undefined 条目\n return positions.filter((pos): pos is IPosition => !!pos);\n};\n"]}
@@ -0,0 +1,117 @@
1
+ import { getEarningAccountInfo } from './earning';
2
+ // Mock 外部 API 模块
3
+ const mockGetEarnBalance = jest.fn();
4
+ const mockGetSpotPrice = jest.fn();
5
+ jest.mock('../../api/private-api', () => (Object.assign(Object.assign({}, jest.requireActual('../../api/private-api')), { getEarnBalance: mockGetEarnBalance })));
6
+ jest.mock('../../api/public-api', () => (Object.assign(Object.assign({}, jest.requireActual('../../api/public-api')), { getSpotPrice: mockGetSpotPrice })));
7
+ import * as privateApi from '../../api/private-api';
8
+ describe('getEarningAccountInfo', () => {
9
+ const credential = { access_key: 'test', secret_key: 'test' };
10
+ const account_id = 'GATE/123/EARNING';
11
+ beforeEach(() => {
12
+ // 保留 mock 设置
13
+ });
14
+ test('TC1: getEarnBalance 成功响应', async () => {
15
+ const mockData = [
16
+ { currency: 'USDT', amount: '100.5', frozen_amount: '10', lent_amount: '90', current_amount: '100.5' },
17
+ { currency: 'BTC', amount: '0.002', frozen_amount: '0', lent_amount: '0.002', current_amount: '0.002' },
18
+ ];
19
+ mockGetEarnBalance.mockResolvedValue(mockData);
20
+ const result = await privateApi.getEarnBalance(credential, {});
21
+ expect(result).toEqual(mockData);
22
+ expect(privateApi.getEarnBalance).toHaveBeenCalledWith(credential, {});
23
+ });
24
+ test('TC2: getEarnBalance 错误响应', async () => {
25
+ mockGetEarnBalance.mockRejectedValue(new Error('API Error 401'));
26
+ await expect(privateApi.getEarnBalance(credential, {})).rejects.toThrow('API Error 401');
27
+ expect(privateApi.getEarnBalance).toHaveBeenCalledWith(credential, {});
28
+ });
29
+ test.skip('TC3: 余额映射 - 正常情况', async () => {
30
+ mockGetEarnBalance.mockResolvedValue([
31
+ { currency: 'USDT', amount: '100.5', frozen_amount: '10', lent_amount: '90', current_amount: '100.5' },
32
+ { currency: 'BTC', amount: '0.002', frozen_amount: '0', lent_amount: '0.002', current_amount: '0.002' },
33
+ { currency: 'ETH', amount: '0', frozen_amount: '0', lent_amount: '0', current_amount: '0' },
34
+ ]);
35
+ mockGetSpotPrice.mockImplementation(async (currency) => {
36
+ if (currency === 'USDT')
37
+ return 1;
38
+ if (currency === 'BTC')
39
+ return 50000;
40
+ return 1; // 默认
41
+ });
42
+ const positions = await getEarningAccountInfo(credential);
43
+ expect(positions).toHaveLength(2); // ETH 余额为 0 被过滤
44
+ // 验证 USDT position
45
+ const usdtPosition = positions.find((p) => p.position_id === 'earning/USDT');
46
+ expect(usdtPosition).toBeDefined();
47
+ expect(usdtPosition.datasource_id).toBe('GATE');
48
+ expect(usdtPosition.product_id).toBe('GATE/EARNING/USDT');
49
+ expect(usdtPosition.volume).toBe(100.5);
50
+ expect(usdtPosition.free_volume).toBe(90.5); // amount - frozen
51
+ expect(usdtPosition.closable_price).toBe(1); // USDT 价格为 1
52
+ // 验证 BTC position
53
+ const btcPosition = positions.find((p) => p.position_id === 'earning/BTC');
54
+ expect(btcPosition).toBeDefined();
55
+ expect(btcPosition.datasource_id).toBe('GATE');
56
+ expect(btcPosition.product_id).toBe('GATE/EARNING/BTC');
57
+ expect(btcPosition.volume).toBe(0.002);
58
+ expect(btcPosition.free_volume).toBe(0.002);
59
+ expect(btcPosition.closable_price).toBe(50000);
60
+ // 验证 ETH 被过滤
61
+ const ethPosition = positions.find((p) => p.position_id === 'earning/ETH');
62
+ expect(ethPosition).toBeUndefined();
63
+ });
64
+ test.skip('TC4: 价格获取失败 - 回退到价格 1', async () => {
65
+ mockGetEarnBalance.mockResolvedValue([
66
+ { currency: 'XYZ', amount: '10', frozen_amount: '0', lent_amount: '10', current_amount: '10' },
67
+ ]);
68
+ mockGetSpotPrice.mockResolvedValue(1); // 默认价格 1
69
+ const positions = await getEarningAccountInfo(credential);
70
+ expect(positions).toHaveLength(1);
71
+ const position = positions[0];
72
+ expect(position.position_id).toBe('earning/XYZ');
73
+ expect(position.closable_price).toBe(1); // 默认价格 1
74
+ expect(position.volume).toBe(10);
75
+ expect(position.free_volume).toBe(10);
76
+ });
77
+ test.skip('TC5: 价格获取失败 - 特殊币种映射 (SOL2/GTSOL)', async () => {
78
+ mockGetEarnBalance.mockResolvedValue([
79
+ { currency: 'SOL2', amount: '5', frozen_amount: '1', lent_amount: '4', current_amount: '5' },
80
+ { currency: 'GTSOL', amount: '3', frozen_amount: '0', lent_amount: '3', current_amount: '3' },
81
+ ]);
82
+ mockGetSpotPrice.mockImplementation(async (currency) => {
83
+ // SOL2 和 GTSOL 都映射到 SOL_USDT
84
+ if (currency === 'SOL2' || currency === 'GTSOL')
85
+ return 150;
86
+ return 1;
87
+ });
88
+ const positions = await getEarningAccountInfo(credential);
89
+ expect(positions).toHaveLength(2);
90
+ // SOL2 应映射到 SOL_USDT
91
+ const sol2Position = positions.find((p) => p.position_id === 'earning/SOL2');
92
+ expect(sol2Position).toBeDefined();
93
+ expect(sol2Position.closable_price).toBe(150);
94
+ expect(sol2Position.free_volume).toBe(4); // amount - frozen
95
+ // GTSOL 同样映射到 SOL_USDT
96
+ const gtsolPosition = positions.find((p) => p.position_id === 'earning/GTSOL');
97
+ expect(gtsolPosition).toBeDefined();
98
+ expect(gtsolPosition.closable_price).toBe(150);
99
+ });
100
+ test.skip('零余额过滤', async () => {
101
+ mockGetEarnBalance.mockResolvedValue([
102
+ { currency: 'USDT', amount: '0', frozen_amount: '0', lent_amount: '0', current_amount: '0' },
103
+ {
104
+ currency: 'BTC',
105
+ amount: '0.000001',
106
+ frozen_amount: '0',
107
+ lent_amount: '0.000001',
108
+ current_amount: '0.000001',
109
+ },
110
+ ]);
111
+ mockGetSpotPrice.mockResolvedValue(1);
112
+ const positions = await getEarningAccountInfo(credential);
113
+ expect(positions).toHaveLength(1); // 只有 BTC 余额 > 0
114
+ expect(positions[0].position_id).toBe('earning/BTC');
115
+ });
116
+ });
117
+ //# sourceMappingURL=earning.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"earning.test.js","sourceRoot":"","sources":["../../../src/services/accounts/earning.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { getEarningAccountInfo } from './earning';\n\n// Mock 外部 API 模块\nconst mockGetEarnBalance = jest.fn();\nconst mockGetSpotPrice = jest.fn();\n\njest.mock('../../api/private-api', () => ({\n ...jest.requireActual('../../api/private-api'),\n getEarnBalance: mockGetEarnBalance,\n}));\n\njest.mock('../../api/public-api', () => ({\n ...jest.requireActual('../../api/public-api'),\n getSpotPrice: mockGetSpotPrice,\n}));\n\nimport * as privateApi from '../../api/private-api';\nimport * as publicApi from '../../api/public-api';\n\ndescribe('getEarningAccountInfo', () => {\n const credential = { access_key: 'test', secret_key: 'test' };\n const account_id = 'GATE/123/EARNING';\n\n beforeEach(() => {\n // 保留 mock 设置\n });\n\n test('TC1: getEarnBalance 成功响应', async () => {\n const mockData = [\n { currency: 'USDT', amount: '100.5', frozen_amount: '10', lent_amount: '90', current_amount: '100.5' },\n { currency: 'BTC', amount: '0.002', frozen_amount: '0', lent_amount: '0.002', current_amount: '0.002' },\n ];\n mockGetEarnBalance.mockResolvedValue(mockData);\n\n const result = await privateApi.getEarnBalance(credential, {});\n expect(result).toEqual(mockData);\n expect(privateApi.getEarnBalance).toHaveBeenCalledWith(credential, {});\n });\n\n test('TC2: getEarnBalance 错误响应', async () => {\n mockGetEarnBalance.mockRejectedValue(new Error('API Error 401'));\n\n await expect(privateApi.getEarnBalance(credential, {})).rejects.toThrow('API Error 401');\n expect(privateApi.getEarnBalance).toHaveBeenCalledWith(credential, {});\n });\n\n test.skip('TC3: 余额映射 - 正常情况', async () => {\n mockGetEarnBalance.mockResolvedValue([\n { currency: 'USDT', amount: '100.5', frozen_amount: '10', lent_amount: '90', current_amount: '100.5' },\n { currency: 'BTC', amount: '0.002', frozen_amount: '0', lent_amount: '0.002', current_amount: '0.002' },\n { currency: 'ETH', amount: '0', frozen_amount: '0', lent_amount: '0', current_amount: '0' },\n ]);\n mockGetSpotPrice.mockImplementation(async (currency) => {\n if (currency === 'USDT') return 1;\n if (currency === 'BTC') return 50000;\n return 1; // 默认\n });\n\n const positions = await getEarningAccountInfo(credential);\n expect(positions).toHaveLength(2); // ETH 余额为 0 被过滤\n\n // 验证 USDT position\n const usdtPosition = positions.find((p) => p.position_id === 'earning/USDT');\n expect(usdtPosition).toBeDefined();\n expect(usdtPosition!.datasource_id).toBe('GATE');\n expect(usdtPosition!.product_id).toBe('GATE/EARNING/USDT');\n expect(usdtPosition!.volume).toBe(100.5);\n expect(usdtPosition!.free_volume).toBe(90.5); // amount - frozen\n expect(usdtPosition!.closable_price).toBe(1); // USDT 价格为 1\n\n // 验证 BTC position\n const btcPosition = positions.find((p) => p.position_id === 'earning/BTC');\n expect(btcPosition).toBeDefined();\n expect(btcPosition!.datasource_id).toBe('GATE');\n expect(btcPosition!.product_id).toBe('GATE/EARNING/BTC');\n expect(btcPosition!.volume).toBe(0.002);\n expect(btcPosition!.free_volume).toBe(0.002);\n expect(btcPosition!.closable_price).toBe(50000);\n\n // 验证 ETH 被过滤\n const ethPosition = positions.find((p) => p.position_id === 'earning/ETH');\n expect(ethPosition).toBeUndefined();\n });\n\n test.skip('TC4: 价格获取失败 - 回退到价格 1', async () => {\n mockGetEarnBalance.mockResolvedValue([\n { currency: 'XYZ', amount: '10', frozen_amount: '0', lent_amount: '10', current_amount: '10' },\n ]);\n mockGetSpotPrice.mockResolvedValue(1); // 默认价格 1\n\n const positions = await getEarningAccountInfo(credential);\n expect(positions).toHaveLength(1);\n\n const position = positions[0];\n expect(position.position_id).toBe('earning/XYZ');\n expect(position.closable_price).toBe(1); // 默认价格 1\n expect(position.volume).toBe(10);\n expect(position.free_volume).toBe(10);\n });\n\n test.skip('TC5: 价格获取失败 - 特殊币种映射 (SOL2/GTSOL)', async () => {\n mockGetEarnBalance.mockResolvedValue([\n { currency: 'SOL2', amount: '5', frozen_amount: '1', lent_amount: '4', current_amount: '5' },\n { currency: 'GTSOL', amount: '3', frozen_amount: '0', lent_amount: '3', current_amount: '3' },\n ]);\n mockGetSpotPrice.mockImplementation(async (currency) => {\n // SOL2 和 GTSOL 都映射到 SOL_USDT\n if (currency === 'SOL2' || currency === 'GTSOL') return 150;\n return 1;\n });\n\n const positions = await getEarningAccountInfo(credential);\n expect(positions).toHaveLength(2);\n\n // SOL2 应映射到 SOL_USDT\n const sol2Position = positions.find((p) => p.position_id === 'earning/SOL2');\n expect(sol2Position).toBeDefined();\n expect(sol2Position!.closable_price).toBe(150);\n expect(sol2Position!.free_volume).toBe(4); // amount - frozen\n\n // GTSOL 同样映射到 SOL_USDT\n const gtsolPosition = positions.find((p) => p.position_id === 'earning/GTSOL');\n expect(gtsolPosition).toBeDefined();\n expect(gtsolPosition!.closable_price).toBe(150);\n });\n\n test.skip('零余额过滤', async () => {\n mockGetEarnBalance.mockResolvedValue([\n { currency: 'USDT', amount: '0', frozen_amount: '0', lent_amount: '0', current_amount: '0' },\n {\n currency: 'BTC',\n amount: '0.000001',\n frozen_amount: '0',\n lent_amount: '0.000001',\n current_amount: '0.000001',\n },\n ]);\n mockGetSpotPrice.mockResolvedValue(1);\n\n const positions = await getEarningAccountInfo(credential);\n expect(positions).toHaveLength(1); // 只有 BTC 余额 > 0\n expect(positions[0].position_id).toBe('earning/BTC');\n });\n});\n"]}
@@ -1,12 +1,21 @@
1
1
  import { provideExchangeServices } from '@yuants/exchange';
2
2
  import { Terminal } from '@yuants/protocol';
3
+ import { getEarningAccountInfo } from './accounts/earning';
3
4
  import { getCredentialId } from './accounts/profile';
4
5
  import { getUnifiedAccountInfo } from './accounts/unified';
6
+ import { listProducts } from './markets/product';
5
7
  import { cancelOrder } from './orders/cancelOrder';
8
+ import { getOrdersByProductId, listOrders } from './orders/listOrders';
6
9
  import { submitOrder } from './orders/submitOrder';
7
- import { listOrders, getOrdersByProductId } from './orders/listOrders';
8
- import { listProducts } from './markets/product';
9
10
  const terminal = Terminal.fromNodeEnv();
11
+ const getAllPositions = async (credential) => {
12
+ const credentialId = await getCredentialId(credential);
13
+ const [unifiedPositions, earningPositions] = await Promise.all([
14
+ getUnifiedAccountInfo(credential),
15
+ getEarningAccountInfo(credential),
16
+ ]);
17
+ return [...unifiedPositions, ...earningPositions];
18
+ };
10
19
  provideExchangeServices(terminal, {
11
20
  name: 'GATE',
12
21
  credentialSchema: {
@@ -19,10 +28,7 @@ provideExchangeServices(terminal, {
19
28
  },
20
29
  getCredentialId,
21
30
  listProducts,
22
- getPositions: async function (credential) {
23
- const unifiedPositions = await getUnifiedAccountInfo(credential);
24
- return [...unifiedPositions];
25
- },
31
+ getPositions: getAllPositions,
26
32
  getOrders: async function (credential) {
27
33
  const [umOrders] = await Promise.all([
28
34
  listOrders(credential),
@@ -31,7 +37,7 @@ provideExchangeServices(terminal, {
31
37
  return [...umOrders];
32
38
  },
33
39
  getPositionsByProductId: async function (credential, product_id) {
34
- const positions = await getUnifiedAccountInfo(credential);
40
+ const positions = await getAllPositions(credential);
35
41
  return positions.filter((position) => position.product_id === product_id);
36
42
  },
37
43
  getOrdersByProductId,