@yuants/vendor-aster 0.9.3 → 0.9.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,35 @@
1
+ import { tokenBucket } from '@yuants/utils';
2
+ // Initialize token buckets at module load time.
3
+ // Subsequent calls should use `tokenBucket(bucketId)` without options.
4
+ // REQUEST_WEIGHT limits (source: exchangeInfo.rateLimits in Aster docs)
5
+ export const futureAPIBucket = tokenBucket('fapi.asterdex.com', {
6
+ capacity: 2400,
7
+ refillInterval: 60000,
8
+ refillAmount: 2400,
9
+ });
10
+ export const spotAPIBucket = tokenBucket('sapi.asterdex.com', {
11
+ capacity: 6000,
12
+ refillInterval: 60000,
13
+ refillAmount: 6000,
14
+ });
15
+ export const orderFutureSecondAPIBucket = tokenBucket('order/future/second', {
16
+ capacity: 300,
17
+ refillInterval: 10000,
18
+ refillAmount: 300,
19
+ });
20
+ export const orderFutureMinuteAPIBucket = tokenBucket('order/future/minute', {
21
+ capacity: 1200,
22
+ refillInterval: 60000,
23
+ refillAmount: 1200,
24
+ });
25
+ export const orderSpotSecondAPIBucket = tokenBucket('order/spot/second', {
26
+ capacity: 1000,
27
+ refillInterval: 10000,
28
+ refillAmount: 1000,
29
+ });
30
+ export const orderSpotMinuteAPIBucket = tokenBucket('order/spot/minute', {
31
+ capacity: 6000,
32
+ refillInterval: 60000,
33
+ refillAmount: 6000,
34
+ });
35
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/api/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAE5C,gDAAgD;AAChD,uEAAuE;AAEvE,wEAAwE;AACxE,MAAM,CAAC,MAAM,eAAe,GAAG,WAAW,CAAC,mBAAmB,EAAE;IAC9D,QAAQ,EAAE,IAAI;IACd,cAAc,EAAE,KAAM;IACtB,YAAY,EAAE,IAAI;CACnB,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,aAAa,GAAG,WAAW,CAAC,mBAAmB,EAAE;IAC5D,QAAQ,EAAE,IAAI;IACd,cAAc,EAAE,KAAM;IACtB,YAAY,EAAE,IAAI;CACnB,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,0BAA0B,GAAG,WAAW,CAAC,qBAAqB,EAAE;IAC3E,QAAQ,EAAE,GAAG;IACb,cAAc,EAAE,KAAM;IACtB,YAAY,EAAE,GAAG;CAClB,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,0BAA0B,GAAG,WAAW,CAAC,qBAAqB,EAAE;IAC3E,QAAQ,EAAE,IAAI;IACd,cAAc,EAAE,KAAM;IACtB,YAAY,EAAE,IAAI;CACnB,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,wBAAwB,GAAG,WAAW,CAAC,mBAAmB,EAAE;IACvE,QAAQ,EAAE,IAAI;IACd,cAAc,EAAE,KAAM;IACtB,YAAY,EAAE,IAAI;CACnB,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,wBAAwB,GAAG,WAAW,CAAC,mBAAmB,EAAE;IACvE,QAAQ,EAAE,IAAI;IACd,cAAc,EAAE,KAAM;IACtB,YAAY,EAAE,IAAI;CACnB,CAAC,CAAC","sourcesContent":["import { tokenBucket } from '@yuants/utils';\n\n// Initialize token buckets at module load time.\n// Subsequent calls should use `tokenBucket(bucketId)` without options.\n\n// REQUEST_WEIGHT limits (source: exchangeInfo.rateLimits in Aster docs)\nexport const futureAPIBucket = tokenBucket('fapi.asterdex.com', {\n capacity: 2400,\n refillInterval: 60_000,\n refillAmount: 2400,\n});\n\nexport const spotAPIBucket = tokenBucket('sapi.asterdex.com', {\n capacity: 6000,\n refillInterval: 60_000,\n refillAmount: 6000,\n});\n\nexport const orderFutureSecondAPIBucket = tokenBucket('order/future/second', {\n capacity: 300,\n refillInterval: 10_000,\n refillAmount: 300,\n});\n\nexport const orderFutureMinuteAPIBucket = tokenBucket('order/future/minute', {\n capacity: 1200,\n refillInterval: 60_000,\n refillAmount: 1200,\n});\n\nexport const orderSpotSecondAPIBucket = tokenBucket('order/spot/second', {\n capacity: 1000,\n refillInterval: 10_000,\n refillAmount: 1000,\n});\n\nexport const orderSpotMinuteAPIBucket = tokenBucket('order/spot/minute', {\n capacity: 6000,\n refillInterval: 60_000,\n refillAmount: 6000,\n});\n"]}
@@ -1,5 +1,6 @@
1
- import { encodeHex, HmacSHA256, newError } from '@yuants/utils';
1
+ import { encodeHex, HmacSHA256, newError, scopeError, tokenBucket } from '@yuants/utils';
2
2
  import { GlobalPrometheusRegistry, Terminal } from '@yuants/protocol';
3
+ import './client';
3
4
  const MetricsAsterApiCallCounter = GlobalPrometheusRegistry.counter('aster_api_call', 'Number of aster api call');
4
5
  const terminal = Terminal.fromNodeEnv();
5
6
  const request = async (credential, method, baseURL, endpoint, params = {}) => {
@@ -39,52 +40,183 @@ const request = async (credential, method, baseURL, endpoint, params = {}) => {
39
40
  }, e);
40
41
  }
41
42
  };
42
- const createApi = (baseURL) => (method, endpoint) => (credential, params) => request(credential, method, baseURL, endpoint, params);
43
- const createFutureApi = createApi('https://fapi.asterdex.com');
44
- const createSpotApi = createApi('https://sapi.asterdex.com');
43
+ const FutureBaseURL = 'https://fapi.asterdex.com';
44
+ const SpotBaseURL = 'https://sapi.asterdex.com';
45
45
  /**
46
46
  * 获取账户信息
47
47
  *
48
+ * Weight: 5
49
+ *
48
50
  * https://github.com/asterdex/api-docs/blob/master/aster-finance-futures-api_CN.md#%E8%B4%A6%E6%88%B7%E4%BF%A1%E6%81%AFv4-user_data
49
51
  */
50
- export const getFApiV4Account = createFutureApi('GET', '/fapi/v4/account');
52
+ export const getFApiV4Account = (credential, params) => {
53
+ const endpoint = '/fapi/v4/account';
54
+ const url = new URL(FutureBaseURL);
55
+ url.pathname = endpoint;
56
+ const weight = 5;
57
+ scopeError('ASTER_API_RATE_LIMIT', { method: 'GET', endpoint, host: url.host, path: url.pathname, bucketId: url.host, weight }, () => tokenBucket(url.host).acquireSync(weight));
58
+ return request(credential, 'GET', FutureBaseURL, endpoint, params);
59
+ };
51
60
  /**
52
61
  * 用户持仓风险V2 (USER_DATA)
53
62
  *
63
+ * Weight: 5
64
+ *
54
65
  * https://github.com/asterdex/api-docs/blob/master/aster-finance-futures-api_CN.md#%E7%94%A8%E6%88%B7%E6%8C%81%E4%BB%93%E9%A3%8E%E9%99%A9v2-user_data
55
66
  */
56
- export const getFApiV2PositionRisk = createFutureApi('GET', '/fapi/v2/positionRisk');
57
- export const getFApiV2Balance = createFutureApi('GET', '/fapi/v2/balance');
58
- export const postFApiV1Order = createFutureApi('POST', '/fapi/v1/order');
67
+ export const getFApiV2PositionRisk = (credential, params) => {
68
+ const endpoint = '/fapi/v2/positionRisk';
69
+ const url = new URL(FutureBaseURL);
70
+ url.pathname = endpoint;
71
+ const weight = 5;
72
+ scopeError('ASTER_API_RATE_LIMIT', { method: 'GET', endpoint, host: url.host, path: url.pathname, bucketId: url.host, weight }, () => tokenBucket(url.host).acquireSync(weight));
73
+ return request(credential, 'GET', FutureBaseURL, endpoint, params);
74
+ };
75
+ /**
76
+ * Weight: 5
77
+ *
78
+ * https://github.com/asterdex/api-docs/blob/master/aster-finance-futures-api_CN.md#L2840-L2855
79
+ */
80
+ export const getFApiV2Balance = (credential, params) => {
81
+ const endpoint = '/fapi/v2/balance';
82
+ const url = new URL(FutureBaseURL);
83
+ url.pathname = endpoint;
84
+ const weight = 5;
85
+ scopeError('ASTER_API_RATE_LIMIT', { method: 'GET', endpoint, host: url.host, path: url.pathname, bucketId: url.host, weight }, () => tokenBucket(url.host).acquireSync(weight));
86
+ return request(credential, 'GET', FutureBaseURL, endpoint, params);
87
+ };
88
+ /**
89
+ * Weight: 1 by order
90
+ *
91
+ * https://github.com/asterdex/api-docs/blob/master/aster-finance-futures-api_CN.md#post-fapiv1order-%E7%9A%84%E7%A4%BA%E4%BE%8B
92
+ */
93
+ export const postFApiV1Order = (credential, params) => {
94
+ const endpoint = '/fapi/v1/order';
95
+ const url = new URL(FutureBaseURL);
96
+ url.pathname = endpoint;
97
+ const weight = 1;
98
+ scopeError('ASTER_FUTURE_ORDER_API_SECOND_RATE_LIMIT', { method: 'POST', endpoint, host: url.host, path: url.pathname, bucketId: url.host, weight }, () => tokenBucket('order/future/second').acquireSync(weight));
99
+ scopeError('ASTER_FUTURE_ORDER_API_MINUTE_RATE_LIMIT', { method: 'POST', endpoint, host: url.host, path: url.pathname, bucketId: url.host, weight }, () => tokenBucket('order/future/minute').acquireSync(weight));
100
+ return request(credential, 'POST', FutureBaseURL, endpoint, params);
101
+ };
59
102
  /**
60
103
  * 查询当前挂单 (永续)
61
104
  *
105
+ * Weight: with symbol 1, without symbol 40
106
+ *
62
107
  * https://github.com/asterdex/api-docs/blob/master/aster-finance-futures-api_CN.md#L2728-L2766
63
108
  */
64
- export const getFApiV1OpenOrders = createFutureApi('GET', '/fapi/v1/openOrders');
109
+ export const getFApiV1OpenOrders = (credential, params) => {
110
+ const endpoint = '/fapi/v1/openOrders';
111
+ const url = new URL(FutureBaseURL);
112
+ url.pathname = endpoint;
113
+ const weight = (params === null || params === void 0 ? void 0 : params.symbol) ? 1 : 40;
114
+ scopeError('ASTER_API_RATE_LIMIT', {
115
+ method: 'GET',
116
+ endpoint,
117
+ host: url.host,
118
+ path: url.pathname,
119
+ bucketId: url.host,
120
+ weight,
121
+ hasSymbol: !!(params === null || params === void 0 ? void 0 : params.symbol),
122
+ }, () => tokenBucket(url.host).acquireSync(weight));
123
+ return request(credential, 'GET', FutureBaseURL, endpoint, params);
124
+ };
65
125
  /**
66
126
  * 查询当前挂单 (现货)
67
127
  *
128
+ * Weight: with symbol 1, without symbol 40
129
+ *
68
130
  * https://github.com/asterdex/api-docs/blob/master/aster-finance-spot-api_CN.md#L1196-L1234
69
131
  */
70
- export const getApiV1OpenOrders = createSpotApi('GET', '/api/v1/openOrders');
71
- export const deleteFApiV1Order = createFutureApi('DELETE', '/fapi/v1/order');
132
+ export const getApiV1OpenOrders = (credential, params) => {
133
+ const endpoint = '/api/v1/openOrders';
134
+ const url = new URL(SpotBaseURL);
135
+ url.pathname = endpoint;
136
+ const weight = (params === null || params === void 0 ? void 0 : params.symbol) ? 1 : 40;
137
+ scopeError('ASTER_API_RATE_LIMIT', {
138
+ method: 'GET',
139
+ endpoint,
140
+ host: url.host,
141
+ path: url.pathname,
142
+ bucketId: url.host,
143
+ weight,
144
+ hasSymbol: !!(params === null || params === void 0 ? void 0 : params.symbol),
145
+ }, () => tokenBucket(url.host).acquireSync(weight));
146
+ return request(credential, 'GET', SpotBaseURL, endpoint, params);
147
+ };
148
+ /**
149
+ * Weight: 1
150
+ *
151
+ * https://github.com/asterdex/api-docs/blob/master/aster-finance-futures-api_CN.md#L2498-L2516
152
+ */
153
+ export const deleteFApiV1Order = (credential, params) => {
154
+ const endpoint = '/fapi/v1/order';
155
+ const url = new URL(FutureBaseURL);
156
+ url.pathname = endpoint;
157
+ const weight = 1;
158
+ scopeError('ASTER_FUTURE_ORDER_API_SECOND_RATE_LIMIT', { method: 'DELETE', endpoint, host: url.host, path: url.pathname, bucketId: url.host, weight }, () => tokenBucket('order/future/second').acquireSync(weight));
159
+ scopeError('ASTER_FUTURE_ORDER_API_MINUTE_RATE_LIMIT', { method: 'DELETE', endpoint, host: url.host, path: url.pathname, bucketId: url.host, weight }, () => tokenBucket('order/future/minute').acquireSync(weight));
160
+ return request(credential, 'DELETE', FutureBaseURL, endpoint, params);
161
+ };
72
162
  /**
73
163
  * 获取账户信息 (现货)
74
164
  *
165
+ * Weight: 5
166
+ *
75
167
  * https://github.com/asterdex/api-docs/blob/master/aster-finance-spot-api_CN.md#%E8%B4%A6%E6%88%B7%E4%BF%A1%E6%81%AF-user_data
76
168
  */
77
- export const getApiV1Account = createSpotApi('GET', '/api/v1/account');
169
+ export const getApiV1Account = (credential, params) => {
170
+ const endpoint = '/api/v1/account';
171
+ const url = new URL(SpotBaseURL);
172
+ url.pathname = endpoint;
173
+ const weight = 5;
174
+ scopeError('ASTER_API_RATE_LIMIT', { method: 'GET', endpoint, host: url.host, path: url.pathname, bucketId: url.host, weight }, () => tokenBucket(url.host).acquireSync(weight));
175
+ return request(credential, 'GET', SpotBaseURL, endpoint, params);
176
+ };
78
177
  /**
79
178
  * 获取最新价格
179
+ *
180
+ * Weight: without symbol 2 (current implementation)
181
+ *
80
182
  * https://github.com/asterdex/api-docs/blob/master/aster-finance-spot-api_CN.md#%E6%9C%80%E6%96%B0%E4%BB%B7%E6%A0%BC
81
183
  */
82
- export const getApiV1TickerPrice = createSpotApi('GET', '/api/v1/ticker/price');
83
- export const postApiV1Order = createSpotApi('POST', '/api/v1/order');
184
+ export const getApiV1TickerPrice = (credential, params) => {
185
+ const endpoint = '/api/v1/ticker/price';
186
+ const url = new URL(SpotBaseURL);
187
+ url.pathname = endpoint;
188
+ const weight = 2;
189
+ scopeError('ASTER_API_RATE_LIMIT', { method: 'GET', endpoint, host: url.host, path: url.pathname, bucketId: url.host, weight }, () => tokenBucket(url.host).acquireSync(weight));
190
+ return request(credential, 'GET', SpotBaseURL, endpoint, params);
191
+ };
192
+ /**
193
+ * Weight: 1
194
+ *
195
+ * https://github.com/asterdex/api-docs/blob/master/aster-finance-spot-api_CN.md#post-apiv1order-%E7%9A%84%E7%A4%BA%E4%BE%8B
196
+ */
197
+ export const postApiV1Order = (credential, params) => {
198
+ const endpoint = '/api/v1/order';
199
+ const url = new URL(SpotBaseURL);
200
+ url.pathname = endpoint;
201
+ const weight = 1;
202
+ scopeError('ASTER_SPOT_ORDER_API_SECOND_RATE_LIMIT', { method: 'POST', endpoint, host: url.host, path: url.pathname, bucketId: url.host, weight }, () => tokenBucket('order/spot/second').acquireSync(weight));
203
+ scopeError('ASTER_SPOT_ORDER_API_MINUTE_RATE_LIMIT', { method: 'POST', endpoint, host: url.host, path: url.pathname, bucketId: url.host, weight }, () => tokenBucket('order/spot/minute').acquireSync(weight));
204
+ return request(credential, 'POST', SpotBaseURL, endpoint, params);
205
+ };
84
206
  /**
85
207
  * 取消有效订单 (现货)
86
208
  *
209
+ * Weight: 1
210
+ *
87
211
  * https://github.com/asterdex/api-docs/blob/master/aster-finance-spot-api_CN.md#L1040-L1074
88
212
  */
89
- export const deleteApiV1Order = createSpotApi('DELETE', '/api/v1/order');
213
+ export const deleteApiV1Order = (credential, params) => {
214
+ const endpoint = '/api/v1/order';
215
+ const url = new URL(SpotBaseURL);
216
+ url.pathname = endpoint;
217
+ const weight = 1;
218
+ scopeError('ASTER_SPOT_ORDER_API_SECOND_RATE_LIMIT', { method: 'DELETE', endpoint, host: url.host, path: url.pathname, bucketId: url.host, weight }, () => tokenBucket('order/spot/second').acquireSync(weight));
219
+ scopeError('ASTER_SPOT_ORDER_API_MINUTE_RATE_LIMIT', { method: 'DELETE', endpoint, host: url.host, path: url.pathname, bucketId: url.host, weight }, () => tokenBucket('order/spot/minute').acquireSync(weight));
220
+ return request(credential, 'DELETE', SpotBaseURL, endpoint, params);
221
+ };
90
222
  //# sourceMappingURL=private-api.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"private-api.js","sourceRoot":"","sources":["../../src/api/private-api.ts"],"names":[],"mappings":"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","sourcesContent":["import { encodeHex, HmacSHA256, newError } from '@yuants/utils';\n\nimport { GlobalPrometheusRegistry, Terminal } from '@yuants/protocol';\n\nconst MetricsAsterApiCallCounter = GlobalPrometheusRegistry.counter(\n 'aster_api_call',\n 'Number of aster api call',\n);\nconst terminal = Terminal.fromNodeEnv();\n\nexport interface ICredential {\n address: string;\n api_key: string;\n secret_key: string;\n}\n\nexport interface IAsterFutureOpenOrder {\n orderId: number;\n clientOrderId: string;\n price: string;\n origQty: string;\n executedQty: string;\n status: string;\n timeInForce: string;\n type: string;\n side: 'BUY' | 'SELL';\n updateTime: number;\n avgPrice: string;\n reduceOnly?: boolean;\n closePosition?: boolean;\n positionSide?: 'BOTH' | 'LONG' | 'SHORT';\n workingType?: string;\n priceProtect?: boolean;\n origType?: string;\n stopPrice?: string;\n symbol: string;\n}\n\nexport interface IAsterSpotOpenOrder {\n orderId: number;\n clientOrderId: string;\n price: string;\n origQty: string;\n executedQty: string;\n cummulativeQuoteQty?: string;\n status: string;\n timeInForce: string;\n type: string;\n side: 'BUY' | 'SELL';\n stopPrice?: string;\n icebergQty?: string;\n time: number;\n updateTime: number;\n isWorking?: boolean;\n avgPrice?: string;\n symbol: string;\n}\n\nconst request = async <T>(\n credential: ICredential,\n method: string,\n baseURL: string,\n endpoint: string,\n params: any = {},\n): Promise<T> => {\n const url = new URL(baseURL);\n url.pathname = endpoint;\n for (const [key, value] of Object.entries(params)) {\n if (value === undefined) continue;\n url.searchParams.set(key, `${value}`);\n }\n\n url.searchParams.set('timestamp', `${Date.now()}`);\n const msg = url.search.slice(1); // 去掉开头的 '?'\n const signature = encodeHex(\n await HmacSHA256(new TextEncoder().encode(msg), new TextEncoder().encode(credential.secret_key)),\n );\n url.searchParams.set('signature', signature);\n\n console.info(url.toString());\n MetricsAsterApiCallCounter.labels({ path: url.pathname, terminal_id: terminal.terminal_id }).inc();\n const response = await fetch(url.toString(), {\n method,\n headers: {\n 'X-MBX-APIKEY': credential.api_key,\n },\n });\n\n const resText = await response.text();\n\n try {\n const res = JSON.parse(resText);\n\n if (res.code && res.code !== 0) {\n throw resText;\n }\n return res;\n } catch (e) {\n throw newError(\n 'ASTER_API_ERROR',\n {\n status: response.status,\n statusText: response.statusText,\n resText,\n params,\n },\n e,\n );\n }\n};\n\nconst createApi =\n (baseURL: string) =>\n <TReq, TRes>(method: string, endpoint: string) =>\n (credential: ICredential, params: TReq) =>\n request<TRes>(credential, method, baseURL, endpoint, params);\n\nconst createFutureApi = createApi('https://fapi.asterdex.com');\nconst createSpotApi = createApi('https://sapi.asterdex.com');\n\n/**\n * 获取账户信息\n *\n * https://github.com/asterdex/api-docs/blob/master/aster-finance-futures-api_CN.md#%E8%B4%A6%E6%88%B7%E4%BF%A1%E6%81%AFv4-user_data\n */\nexport const getFApiV4Account = createFutureApi<\n {},\n {\n feeTier: number;\n canTrade: boolean;\n canDeposit: boolean;\n canWithdraw: boolean;\n updateTime: number;\n totalInitialMargin: string;\n totalMaintMargin: string;\n totalWalletBalance: string;\n totalUnrealizedProfit: string;\n totalMarginBalance: string;\n totalPositionInitialMargin: string;\n totalOpenOrderInitialMargin: string;\n totalCrossWalletBalance: string;\n totalCrossUnPnl: string;\n availableBalance: string;\n maxWithdrawAmount: string;\n assets: {\n asset: string;\n walletBalance: string;\n unrealizedProfit: string;\n marginBalance: string;\n maintMargin: string;\n initialMargin: string;\n positionInitialMargin: string;\n openOrderInitialMargin: string;\n maxWithdrawAmount: string;\n crossWalletBalance: string;\n crossUnPnl: string;\n availableBalance: string;\n marginAvailable: boolean;\n updateTime: number;\n }[];\n positions: {\n symbol: string;\n initialMargin: string;\n maintMargin: string;\n unrealizedProfit: string;\n positionInitialMargin: string;\n openOrderInitialMargin: string;\n leverage: string;\n isolated: boolean;\n entryPrice: string;\n maxNotional: string;\n positionSide: 'BOTH' | 'LONG' | 'SHORT';\n positionAmt: string;\n notional: string;\n isolatedWallet: string;\n updateTime: number;\n }[];\n }\n>('GET', '/fapi/v4/account');\n\n/**\n * 用户持仓风险V2 (USER_DATA)\n *\n * https://github.com/asterdex/api-docs/blob/master/aster-finance-futures-api_CN.md#%E7%94%A8%E6%88%B7%E6%8C%81%E4%BB%93%E9%A3%8E%E9%99%A9v2-user_data\n */\nexport const getFApiV2PositionRisk = createFutureApi<\n {\n symbol?: string;\n },\n {\n entryPrice: string;\n marginType: string;\n isAutoAddMargin: string;\n isolatedMargin: string;\n leverage: string;\n liquidationPrice: string;\n markPrice: string;\n maxNotionalValue: string;\n positionAmt: string;\n symbol: string;\n unRealizedProfit: string;\n positionSide: string;\n updateTime: number;\n }[]\n>('GET', '/fapi/v2/positionRisk');\n\nexport const getFApiV2Balance = createFutureApi<\n {},\n {\n accountAlias: string; // 账户唯一识别码\n asset: string; // 资产\n balance: string; // 总余额\n crossWalletBalance: string; // 全仓余额\n crossUnPnl: string; // 全仓持仓未实现盈亏\n availableBalance: string; // 下单可用余额\n maxWithdrawAmount: string; // 最大可转出余额\n marginAvailable: boolean; // 是否可用作联合保证金\n updateTime: number;\n }[]\n>('GET', '/fapi/v2/balance');\n\nexport const postFApiV1Order = createFutureApi<\n {\n symbol: string;\n side: 'BUY' | 'SELL';\n positionSide?: 'BOTH' | 'LONG' | 'SHORT';\n type:\n | 'MARKET'\n | 'LIMIT'\n | 'STOP'\n | 'STOP_MARKET'\n | 'TAKE_PROFIT'\n | 'TAKE_PROFIT_MARKET'\n | 'TRAILING_STOP_MARKET';\n reduceOnly?: 'true' | 'false';\n quantity?: number;\n price?: number;\n timeInForce?: 'GTC' | 'IOC' | 'FOK' | 'GTX' | 'HIDDEN';\n },\n {}\n>('POST', '/fapi/v1/order');\n\n/**\n * 查询当前挂单 (永续)\n *\n * https://github.com/asterdex/api-docs/blob/master/aster-finance-futures-api_CN.md#L2728-L2766\n */\nexport const getFApiV1OpenOrders = createFutureApi<\n {\n symbol?: string;\n },\n IAsterFutureOpenOrder[]\n>('GET', '/fapi/v1/openOrders');\n\n/**\n * 查询当前挂单 (现货)\n *\n * https://github.com/asterdex/api-docs/blob/master/aster-finance-spot-api_CN.md#L1196-L1234\n */\nexport const getApiV1OpenOrders = createSpotApi<\n {\n symbol?: string;\n },\n IAsterSpotOpenOrder[]\n>('GET', '/api/v1/openOrders');\n\nexport const deleteFApiV1Order = createFutureApi<\n {\n symbol: string;\n orderId?: string | number;\n origClientOrderId?: string;\n },\n {}\n>('DELETE', '/fapi/v1/order');\n\n/**\n * 获取账户信息 (现货)\n *\n * https://github.com/asterdex/api-docs/blob/master/aster-finance-spot-api_CN.md#%E8%B4%A6%E6%88%B7%E4%BF%A1%E6%81%AF-user_data\n */\nexport const getApiV1Account = createSpotApi<\n {},\n {\n feeTier: number;\n canTrade: boolean;\n canDeposit: boolean;\n canWithdraw: boolean;\n canBurnAsset: boolean;\n updateTime: number;\n balances: {\n asset: string;\n free: string;\n locked: string;\n }[];\n }\n>('GET', '/api/v1/account');\n\n/**\n * 获取最新价格\n * https://github.com/asterdex/api-docs/blob/master/aster-finance-spot-api_CN.md#%E6%9C%80%E6%96%B0%E4%BB%B7%E6%A0%BC\n */\nexport const getApiV1TickerPrice = createSpotApi<\n {},\n {\n symbol: string;\n price: string;\n time: number;\n }[]\n>('GET', '/api/v1/ticker/price');\n\nexport const postApiV1Order = createSpotApi<\n {\n symbol: string;\n side: 'BUY' | 'SELL';\n type: 'MARKET' | 'LIMIT' | 'STOP' | 'STOP_MARKET' | 'TAKE_PROFIT' | 'TAKE_PROFIT_MARKET';\n timeInForce?: 'GTC' | 'IOC' | 'FOK' | 'GTX';\n quantity?: number;\n quoteOrderQty?: number;\n price?: number;\n },\n {\n orderId: number; // 系统的订单ID\n }\n>('POST', '/api/v1/order');\n\n/**\n * 取消有效订单 (现货)\n *\n * https://github.com/asterdex/api-docs/blob/master/aster-finance-spot-api_CN.md#L1040-L1074\n */\nexport const deleteApiV1Order = createSpotApi<\n {\n symbol: string;\n orderId?: string | number;\n origClientOrderId?: string;\n },\n {}\n>('DELETE', '/api/v1/order');\n"]}
1
+ {"version":3,"file":"private-api.js","sourceRoot":"","sources":["../../src/api/private-api.ts"],"names":[],"mappings":"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","sourcesContent":["import { encodeHex, HmacSHA256, newError, scopeError, tokenBucket } from '@yuants/utils';\n\nimport { GlobalPrometheusRegistry, Terminal } from '@yuants/protocol';\n\nimport './client';\n\nconst MetricsAsterApiCallCounter = GlobalPrometheusRegistry.counter(\n 'aster_api_call',\n 'Number of aster api call',\n);\nconst terminal = Terminal.fromNodeEnv();\n\nexport interface ICredential {\n address: string;\n api_key: string;\n secret_key: string;\n}\n\nexport interface IAsterFutureOpenOrder {\n orderId: number;\n clientOrderId: string;\n price: string;\n origQty: string;\n executedQty: string;\n status: string;\n timeInForce: string;\n type: string;\n side: 'BUY' | 'SELL';\n updateTime: number;\n avgPrice: string;\n reduceOnly?: boolean;\n closePosition?: boolean;\n positionSide?: 'BOTH' | 'LONG' | 'SHORT';\n workingType?: string;\n priceProtect?: boolean;\n origType?: string;\n stopPrice?: string;\n symbol: string;\n}\n\nexport interface IAsterSpotOpenOrder {\n orderId: number;\n clientOrderId: string;\n price: string;\n origQty: string;\n executedQty: string;\n cummulativeQuoteQty?: string;\n status: string;\n timeInForce: string;\n type: string;\n side: 'BUY' | 'SELL';\n stopPrice?: string;\n icebergQty?: string;\n time: number;\n updateTime: number;\n isWorking?: boolean;\n avgPrice?: string;\n symbol: string;\n}\n\nconst request = async <T>(\n credential: ICredential,\n method: string,\n baseURL: string,\n endpoint: string,\n params: Record<string, unknown> = {},\n): Promise<T> => {\n const url = new URL(baseURL);\n url.pathname = endpoint;\n for (const [key, value] of Object.entries(params)) {\n if (value === undefined) continue;\n url.searchParams.set(key, `${value}`);\n }\n\n url.searchParams.set('timestamp', `${Date.now()}`);\n const msg = url.search.slice(1); // 去掉开头的 '?'\n const signature = encodeHex(\n await HmacSHA256(new TextEncoder().encode(msg), new TextEncoder().encode(credential.secret_key)),\n );\n url.searchParams.set('signature', signature);\n\n console.info(url.toString());\n MetricsAsterApiCallCounter.labels({ path: url.pathname, terminal_id: terminal.terminal_id }).inc();\n const response = await fetch(url.toString(), {\n method,\n headers: {\n 'X-MBX-APIKEY': credential.api_key,\n },\n });\n\n const resText = await response.text();\n\n try {\n const res = JSON.parse(resText);\n\n if (res.code && res.code !== 0) {\n throw resText;\n }\n return res;\n } catch (e) {\n throw newError(\n 'ASTER_API_ERROR',\n {\n status: response.status,\n statusText: response.statusText,\n resText,\n params,\n },\n e,\n );\n }\n};\n\nconst FutureBaseURL = 'https://fapi.asterdex.com';\nconst SpotBaseURL = 'https://sapi.asterdex.com';\n\n/**\n * 获取账户信息\n *\n * Weight: 5\n *\n * https://github.com/asterdex/api-docs/blob/master/aster-finance-futures-api_CN.md#%E8%B4%A6%E6%88%B7%E4%BF%A1%E6%81%AFv4-user_data\n */\nexport const getFApiV4Account = (\n credential: ICredential,\n params: Record<string, never>,\n): Promise<{\n feeTier: number;\n canTrade: boolean;\n canDeposit: boolean;\n canWithdraw: boolean;\n updateTime: number;\n totalInitialMargin: string;\n totalMaintMargin: string;\n totalWalletBalance: string;\n totalUnrealizedProfit: string;\n totalMarginBalance: string;\n totalPositionInitialMargin: string;\n totalOpenOrderInitialMargin: string;\n totalCrossWalletBalance: string;\n totalCrossUnPnl: string;\n availableBalance: string;\n maxWithdrawAmount: string;\n assets: {\n asset: string;\n walletBalance: string;\n unrealizedProfit: string;\n marginBalance: string;\n maintMargin: string;\n initialMargin: string;\n positionInitialMargin: string;\n openOrderInitialMargin: string;\n maxWithdrawAmount: string;\n crossWalletBalance: string;\n crossUnPnl: string;\n availableBalance: string;\n marginAvailable: boolean;\n updateTime: number;\n }[];\n positions: {\n symbol: string;\n initialMargin: string;\n maintMargin: string;\n unrealizedProfit: string;\n positionInitialMargin: string;\n openOrderInitialMargin: string;\n leverage: string;\n isolated: boolean;\n entryPrice: string;\n maxNotional: string;\n positionSide: 'BOTH' | 'LONG' | 'SHORT';\n positionAmt: string;\n notional: string;\n isolatedWallet: string;\n updateTime: number;\n }[];\n}> => {\n const endpoint = '/fapi/v4/account';\n const url = new URL(FutureBaseURL);\n url.pathname = endpoint;\n const weight = 5;\n scopeError(\n 'ASTER_API_RATE_LIMIT',\n { method: 'GET', endpoint, host: url.host, path: url.pathname, bucketId: url.host, weight },\n () => tokenBucket(url.host).acquireSync(weight),\n );\n return request(credential, 'GET', FutureBaseURL, endpoint, params);\n};\n\n/**\n * 用户持仓风险V2 (USER_DATA)\n *\n * Weight: 5\n *\n * https://github.com/asterdex/api-docs/blob/master/aster-finance-futures-api_CN.md#%E7%94%A8%E6%88%B7%E6%8C%81%E4%BB%93%E9%A3%8E%E9%99%A9v2-user_data\n */\nexport const getFApiV2PositionRisk = (\n credential: ICredential,\n params: {\n symbol?: string;\n },\n): Promise<\n {\n entryPrice: string;\n marginType: string;\n isAutoAddMargin: string;\n isolatedMargin: string;\n leverage: string;\n liquidationPrice: string;\n markPrice: string;\n maxNotionalValue: string;\n positionAmt: string;\n symbol: string;\n unRealizedProfit: string;\n positionSide: string;\n updateTime: number;\n }[]\n> => {\n const endpoint = '/fapi/v2/positionRisk';\n const url = new URL(FutureBaseURL);\n url.pathname = endpoint;\n const weight = 5;\n scopeError(\n 'ASTER_API_RATE_LIMIT',\n { method: 'GET', endpoint, host: url.host, path: url.pathname, bucketId: url.host, weight },\n () => tokenBucket(url.host).acquireSync(weight),\n );\n return request(credential, 'GET', FutureBaseURL, endpoint, params);\n};\n\n/**\n * Weight: 5\n *\n * https://github.com/asterdex/api-docs/blob/master/aster-finance-futures-api_CN.md#L2840-L2855\n */\nexport const getFApiV2Balance = (\n credential: ICredential,\n params: Record<string, never>,\n): Promise<\n {\n accountAlias: string;\n asset: string;\n balance: string;\n crossWalletBalance: string;\n crossUnPnl: string;\n availableBalance: string;\n maxWithdrawAmount: string;\n marginAvailable: boolean;\n updateTime: number;\n }[]\n> => {\n const endpoint = '/fapi/v2/balance';\n const url = new URL(FutureBaseURL);\n url.pathname = endpoint;\n const weight = 5;\n scopeError(\n 'ASTER_API_RATE_LIMIT',\n { method: 'GET', endpoint, host: url.host, path: url.pathname, bucketId: url.host, weight },\n () => tokenBucket(url.host).acquireSync(weight),\n );\n return request(credential, 'GET', FutureBaseURL, endpoint, params);\n};\n\n/**\n * Weight: 1 by order\n *\n * https://github.com/asterdex/api-docs/blob/master/aster-finance-futures-api_CN.md#post-fapiv1order-%E7%9A%84%E7%A4%BA%E4%BE%8B\n */\nexport const postFApiV1Order = (\n credential: ICredential,\n params: {\n symbol: string;\n side: 'BUY' | 'SELL';\n positionSide?: 'BOTH' | 'LONG' | 'SHORT';\n type:\n | 'MARKET'\n | 'LIMIT'\n | 'STOP'\n | 'STOP_MARKET'\n | 'TAKE_PROFIT'\n | 'TAKE_PROFIT_MARKET'\n | 'TRAILING_STOP_MARKET';\n reduceOnly?: 'true' | 'false';\n quantity?: number;\n price?: number;\n timeInForce?: 'GTC' | 'IOC' | 'FOK' | 'GTX' | 'HIDDEN';\n },\n): Promise<Record<string, never>> => {\n const endpoint = '/fapi/v1/order';\n const url = new URL(FutureBaseURL);\n url.pathname = endpoint;\n const weight = 1;\n scopeError(\n 'ASTER_FUTURE_ORDER_API_SECOND_RATE_LIMIT',\n { method: 'POST', endpoint, host: url.host, path: url.pathname, bucketId: url.host, weight },\n () => tokenBucket('order/future/second').acquireSync(weight),\n );\n scopeError(\n 'ASTER_FUTURE_ORDER_API_MINUTE_RATE_LIMIT',\n { method: 'POST', endpoint, host: url.host, path: url.pathname, bucketId: url.host, weight },\n () => tokenBucket('order/future/minute').acquireSync(weight),\n );\n return request(credential, 'POST', FutureBaseURL, endpoint, params);\n};\n\n/**\n * 查询当前挂单 (永续)\n *\n * Weight: with symbol 1, without symbol 40\n *\n * https://github.com/asterdex/api-docs/blob/master/aster-finance-futures-api_CN.md#L2728-L2766\n */\nexport const getFApiV1OpenOrders = (\n credential: ICredential,\n params: {\n symbol?: string;\n },\n): Promise<IAsterFutureOpenOrder[]> => {\n const endpoint = '/fapi/v1/openOrders';\n const url = new URL(FutureBaseURL);\n url.pathname = endpoint;\n const weight = params?.symbol ? 1 : 40;\n scopeError(\n 'ASTER_API_RATE_LIMIT',\n {\n method: 'GET',\n endpoint,\n host: url.host,\n path: url.pathname,\n bucketId: url.host,\n weight,\n hasSymbol: !!params?.symbol,\n },\n () => tokenBucket(url.host).acquireSync(weight),\n );\n return request(credential, 'GET', FutureBaseURL, endpoint, params);\n};\n\n/**\n * 查询当前挂单 (现货)\n *\n * Weight: with symbol 1, without symbol 40\n *\n * https://github.com/asterdex/api-docs/blob/master/aster-finance-spot-api_CN.md#L1196-L1234\n */\nexport const getApiV1OpenOrders = (\n credential: ICredential,\n params: {\n symbol?: string;\n },\n): Promise<IAsterSpotOpenOrder[]> => {\n const endpoint = '/api/v1/openOrders';\n const url = new URL(SpotBaseURL);\n url.pathname = endpoint;\n const weight = params?.symbol ? 1 : 40;\n scopeError(\n 'ASTER_API_RATE_LIMIT',\n {\n method: 'GET',\n endpoint,\n host: url.host,\n path: url.pathname,\n bucketId: url.host,\n weight,\n hasSymbol: !!params?.symbol,\n },\n () => tokenBucket(url.host).acquireSync(weight),\n );\n return request(credential, 'GET', SpotBaseURL, endpoint, params);\n};\n\n/**\n * Weight: 1\n *\n * https://github.com/asterdex/api-docs/blob/master/aster-finance-futures-api_CN.md#L2498-L2516\n */\nexport const deleteFApiV1Order = (\n credential: ICredential,\n params: {\n symbol: string;\n orderId?: string | number;\n origClientOrderId?: string;\n },\n): Promise<Record<string, never>> => {\n const endpoint = '/fapi/v1/order';\n const url = new URL(FutureBaseURL);\n url.pathname = endpoint;\n const weight = 1;\n scopeError(\n 'ASTER_FUTURE_ORDER_API_SECOND_RATE_LIMIT',\n { method: 'DELETE', endpoint, host: url.host, path: url.pathname, bucketId: url.host, weight },\n () => tokenBucket('order/future/second').acquireSync(weight),\n );\n scopeError(\n 'ASTER_FUTURE_ORDER_API_MINUTE_RATE_LIMIT',\n { method: 'DELETE', endpoint, host: url.host, path: url.pathname, bucketId: url.host, weight },\n () => tokenBucket('order/future/minute').acquireSync(weight),\n );\n return request(credential, 'DELETE', FutureBaseURL, endpoint, params);\n};\n\n/**\n * 获取账户信息 (现货)\n *\n * Weight: 5\n *\n * https://github.com/asterdex/api-docs/blob/master/aster-finance-spot-api_CN.md#%E8%B4%A6%E6%88%B7%E4%BF%A1%E6%81%AF-user_data\n */\nexport const getApiV1Account = (\n credential: ICredential,\n params: Record<string, never>,\n): Promise<{\n feeTier: number;\n canTrade: boolean;\n canDeposit: boolean;\n canWithdraw: boolean;\n canBurnAsset: boolean;\n updateTime: number;\n balances: {\n asset: string;\n free: string;\n locked: string;\n }[];\n}> => {\n const endpoint = '/api/v1/account';\n const url = new URL(SpotBaseURL);\n url.pathname = endpoint;\n const weight = 5;\n scopeError(\n 'ASTER_API_RATE_LIMIT',\n { method: 'GET', endpoint, host: url.host, path: url.pathname, bucketId: url.host, weight },\n () => tokenBucket(url.host).acquireSync(weight),\n );\n return request(credential, 'GET', SpotBaseURL, endpoint, params);\n};\n\n/**\n * 获取最新价格\n *\n * Weight: without symbol 2 (current implementation)\n *\n * https://github.com/asterdex/api-docs/blob/master/aster-finance-spot-api_CN.md#%E6%9C%80%E6%96%B0%E4%BB%B7%E6%A0%BC\n */\nexport const getApiV1TickerPrice = (\n credential: ICredential,\n params: Record<string, never>,\n): Promise<\n {\n symbol: string;\n price: string;\n time: number;\n }[]\n> => {\n const endpoint = '/api/v1/ticker/price';\n const url = new URL(SpotBaseURL);\n url.pathname = endpoint;\n const weight = 2;\n scopeError(\n 'ASTER_API_RATE_LIMIT',\n { method: 'GET', endpoint, host: url.host, path: url.pathname, bucketId: url.host, weight },\n () => tokenBucket(url.host).acquireSync(weight),\n );\n return request(credential, 'GET', SpotBaseURL, endpoint, params);\n};\n\n/**\n * Weight: 1\n *\n * https://github.com/asterdex/api-docs/blob/master/aster-finance-spot-api_CN.md#post-apiv1order-%E7%9A%84%E7%A4%BA%E4%BE%8B\n */\nexport const postApiV1Order = (\n credential: ICredential,\n params: {\n symbol: string;\n side: 'BUY' | 'SELL';\n type: 'MARKET' | 'LIMIT' | 'STOP' | 'STOP_MARKET' | 'TAKE_PROFIT' | 'TAKE_PROFIT_MARKET';\n timeInForce?: 'GTC' | 'IOC' | 'FOK' | 'GTX';\n quantity?: number;\n quoteOrderQty?: number;\n price?: number;\n },\n): Promise<{\n orderId: number;\n}> => {\n const endpoint = '/api/v1/order';\n const url = new URL(SpotBaseURL);\n url.pathname = endpoint;\n const weight = 1;\n scopeError(\n 'ASTER_SPOT_ORDER_API_SECOND_RATE_LIMIT',\n { method: 'POST', endpoint, host: url.host, path: url.pathname, bucketId: url.host, weight },\n () => tokenBucket('order/spot/second').acquireSync(weight),\n );\n scopeError(\n 'ASTER_SPOT_ORDER_API_MINUTE_RATE_LIMIT',\n { method: 'POST', endpoint, host: url.host, path: url.pathname, bucketId: url.host, weight },\n () => tokenBucket('order/spot/minute').acquireSync(weight),\n );\n return request(credential, 'POST', SpotBaseURL, endpoint, params);\n};\n\n/**\n * 取消有效订单 (现货)\n *\n * Weight: 1\n *\n * https://github.com/asterdex/api-docs/blob/master/aster-finance-spot-api_CN.md#L1040-L1074\n */\nexport const deleteApiV1Order = (\n credential: ICredential,\n params: {\n symbol: string;\n orderId?: string | number;\n origClientOrderId?: string;\n },\n): Promise<Record<string, never>> => {\n const endpoint = '/api/v1/order';\n const url = new URL(SpotBaseURL);\n url.pathname = endpoint;\n const weight = 1;\n scopeError(\n 'ASTER_SPOT_ORDER_API_SECOND_RATE_LIMIT',\n { method: 'DELETE', endpoint, host: url.host, path: url.pathname, bucketId: url.host, weight },\n () => tokenBucket('order/spot/second').acquireSync(weight),\n );\n scopeError(\n 'ASTER_SPOT_ORDER_API_MINUTE_RATE_LIMIT',\n { method: 'DELETE', endpoint, host: url.host, path: url.pathname, bucketId: url.host, weight },\n () => tokenBucket('order/spot/minute').acquireSync(weight),\n );\n return request(credential, 'DELETE', SpotBaseURL, endpoint, params);\n};\n"]}
@@ -1,6 +1,20 @@
1
1
  import { GlobalPrometheusRegistry, Terminal } from '@yuants/protocol';
2
+ import { scopeError, tokenBucket } from '@yuants/utils';
3
+ import './client';
2
4
  const MetricsAsterApiCallCounter = GlobalPrometheusRegistry.counter('aster_api_call', 'Number of aster api call');
3
5
  const terminal = Terminal.fromNodeEnv();
6
+ const FutureBaseURL = 'https://fapi.asterdex.com';
7
+ const SpotBaseURL = 'https://sapi.asterdex.com';
8
+ const getKlinesRequestWeight = (limit) => {
9
+ const resolvedLimit = limit !== null && limit !== void 0 ? limit : 500;
10
+ if (resolvedLimit < 100)
11
+ return 1;
12
+ if (resolvedLimit < 500)
13
+ return 2;
14
+ if (resolvedLimit <= 1000)
15
+ return 5;
16
+ return 10;
17
+ };
4
18
  const request = async (method, baseUrl, endpoint, params = {}) => {
5
19
  const url = new URL(baseUrl);
6
20
  url.pathname = endpoint;
@@ -20,57 +34,146 @@ const request = async (method, baseUrl, endpoint, params = {}) => {
20
34
  }
21
35
  return res;
22
36
  };
23
- const createApi = (baseUrl) => (method, endpoint) => (params) => request(method, baseUrl, endpoint, params);
24
- const createFutureApi = createApi('https://fapi.asterdex.com');
25
- const createSpotApi = createApi('https://sapi.asterdex.com');
26
37
  /**
27
38
  * 获取资金费率历史
28
39
  *
40
+ * Weight: 1
41
+ *
29
42
  * https://github.com/asterdex/api-docs/blob/master/aster-finance-futures-api_CN.md#%E6%9F%A5%E8%AF%A2%E8%B5%84%E9%87%91%E8%B4%B9%E7%8E%87%E5%8E%86%E5%8F%B2
30
43
  */
31
- export const getFApiV1FundingRate = createFutureApi('GET', '/fapi/v1/fundingRate');
44
+ export const getFApiV1FundingRate = (params) => {
45
+ const endpoint = '/fapi/v1/fundingRate';
46
+ const url = new URL(FutureBaseURL);
47
+ url.pathname = endpoint;
48
+ const weight = 1;
49
+ scopeError('ASTER_API_RATE_LIMIT', { method: 'GET', endpoint, host: url.host, path: url.pathname, bucketId: url.host, weight }, () => tokenBucket(url.host).acquireSync(weight));
50
+ return request('GET', FutureBaseURL, endpoint, params);
51
+ };
32
52
  /**
33
53
  * 获取交易对信息
34
54
  *
55
+ * Weight: 1
56
+ *
35
57
  * https://github.com/asterdex/api-docs/blob/master/aster-finance-futures-api_CN.md#%E4%BA%A4%E6%98%93%E5%AF%B9%E4%BF%A1%E6%81%AF
36
58
  */
37
- export const getFApiV1ExchangeInfo = createFutureApi('GET', '/fapi/v1/exchangeInfo');
59
+ export const getFApiV1ExchangeInfo = (params) => {
60
+ const endpoint = '/fapi/v1/exchangeInfo';
61
+ const url = new URL(FutureBaseURL);
62
+ url.pathname = endpoint;
63
+ const weight = 1;
64
+ scopeError('ASTER_API_RATE_LIMIT', { method: 'GET', endpoint, host: url.host, path: url.pathname, bucketId: url.host, weight }, () => tokenBucket(url.host).acquireSync(weight));
65
+ return request('GET', FutureBaseURL, endpoint, params);
66
+ };
38
67
  /**
39
68
  * 获取现货交易对信息
40
69
  *
70
+ * Weight: 1
71
+ *
41
72
  * https://github.com/asterdex/api-docs/blob/master/aster-finance-spot-api_CN.md#L1080-L1145
42
73
  */
43
- export const getApiV1ExchangeInfo = createSpotApi('GET', '/api/v1/exchangeInfo');
74
+ export const getApiV1ExchangeInfo = (params) => {
75
+ const endpoint = '/api/v1/exchangeInfo';
76
+ const url = new URL(SpotBaseURL);
77
+ url.pathname = endpoint;
78
+ const weight = 1;
79
+ scopeError('ASTER_API_RATE_LIMIT', { method: 'GET', endpoint, host: url.host, path: url.pathname, bucketId: url.host, weight }, () => tokenBucket(url.host).acquireSync(weight));
80
+ return request('GET', SpotBaseURL, endpoint, params);
81
+ };
44
82
  /**
45
83
  * 获取未平仓合约数量
46
84
  *
47
85
  * 无 API 文档 (weird)
86
+ * 参考 Binance 风格接口:/fapi/v1/openInterest
87
+ *
88
+ * Weight: 1
89
+ * https://developers.binance.com/docs/zh-CN/derivatives/usds-margined-futures/market-data/rest-api/Open-Interest
48
90
  */
49
- export const getFApiV1OpenInterest = createFutureApi('GET', '/fapi/v1/openInterest');
91
+ export const getFApiV1OpenInterest = (params) => {
92
+ const endpoint = '/fapi/v1/openInterest';
93
+ const url = new URL(FutureBaseURL);
94
+ url.pathname = endpoint;
95
+ const weight = 1;
96
+ scopeError('ASTER_API_RATE_LIMIT', { method: 'GET', endpoint, host: url.host, path: url.pathname, bucketId: url.host, weight }, () => tokenBucket(url.host).acquireSync(weight));
97
+ return request('GET', FutureBaseURL, endpoint, params);
98
+ };
50
99
  /**
51
100
  * 获取最新价格
52
101
  *
102
+ * Weight: without symbol 2 (current implementation)
103
+ *
53
104
  * https://github.com/asterdex/api-docs/blob/master/aster-finance-futures-api_CN.md#%E6%9C%80%E6%96%B0%E4%BB%B7%E6%A0%BC
54
105
  */
55
- export const getFApiV1TickerPrice = createFutureApi('GET', '/fapi/v1/ticker/price');
106
+ export const getFApiV1TickerPrice = (params) => {
107
+ const endpoint = '/fapi/v1/ticker/price';
108
+ const url = new URL(FutureBaseURL);
109
+ url.pathname = endpoint;
110
+ const weight = 2;
111
+ scopeError('ASTER_API_RATE_LIMIT', { method: 'GET', endpoint, host: url.host, path: url.pathname, bucketId: url.host, weight }, () => tokenBucket(url.host).acquireSync(weight));
112
+ return request('GET', FutureBaseURL, endpoint, params);
113
+ };
56
114
  /**
57
115
  * 获取资金费率
116
+ *
117
+ * Weight: 1
118
+ *
58
119
  * https://github.com/asterdex/api-docs/blob/master/aster-finance-futures-api_CN.md
59
120
  */
60
- export const getFApiV1PremiumIndex = createFutureApi('GET', '/fapi/v1/premiumIndex');
121
+ export const getFApiV1PremiumIndex = (params) => {
122
+ const endpoint = '/fapi/v1/premiumIndex';
123
+ const url = new URL(FutureBaseURL);
124
+ url.pathname = endpoint;
125
+ const weight = 1;
126
+ scopeError('ASTER_API_RATE_LIMIT', { method: 'GET', endpoint, host: url.host, path: url.pathname, bucketId: url.host, weight }, () => tokenBucket(url.host).acquireSync(weight));
127
+ return request('GET', FutureBaseURL, endpoint, params);
128
+ };
61
129
  /**
62
130
  * 获取 K 线
63
131
  *
64
132
  * 参考 Binance 风格接口:/fapi/v1/klines
133
+ *
134
+ * Weight: by limit
135
+ *
65
136
  * https://github.com/asterdex/api-docs/blob/master/aster-finance-futures-api_CN.md#k%E7%BA%BF%E6%95%B0%E6%8D%AE
66
137
  */
67
- export const getFApiV1Klines = createFutureApi('GET', '/fapi/v1/klines');
138
+ export const getFApiV1Klines = (params) => {
139
+ const endpoint = '/fapi/v1/klines';
140
+ const url = new URL(FutureBaseURL);
141
+ url.pathname = endpoint;
142
+ const weight = getKlinesRequestWeight(params === null || params === void 0 ? void 0 : params.limit);
143
+ scopeError('ASTER_API_RATE_LIMIT', {
144
+ method: 'GET',
145
+ endpoint,
146
+ host: url.host,
147
+ path: url.pathname,
148
+ bucketId: url.host,
149
+ weight,
150
+ limit: params === null || params === void 0 ? void 0 : params.limit,
151
+ }, () => tokenBucket(url.host).acquireSync(weight));
152
+ return request('GET', FutureBaseURL, endpoint, params);
153
+ };
68
154
  /**
69
155
  * 获取现货 K 线
70
156
  *
71
157
  * 参考 Binance 风格接口:/api/v1/klines
72
158
  *
159
+ * Weight: not documented (temporary: follow futures limit table)
160
+ *
73
161
  * https://github.com/asterdex/api-docs/blob/master/aster-finance-spot-api_CN.md
74
162
  */
75
- export const getApiV1Klines = createSpotApi('GET', '/api/v1/klines');
163
+ export const getApiV1Klines = (params) => {
164
+ const endpoint = '/api/v1/klines';
165
+ const url = new URL(SpotBaseURL);
166
+ url.pathname = endpoint;
167
+ const weight = getKlinesRequestWeight(params === null || params === void 0 ? void 0 : params.limit);
168
+ scopeError('ASTER_API_RATE_LIMIT', {
169
+ method: 'GET',
170
+ endpoint,
171
+ host: url.host,
172
+ path: url.pathname,
173
+ bucketId: url.host,
174
+ weight,
175
+ limit: params === null || params === void 0 ? void 0 : params.limit,
176
+ }, () => tokenBucket(url.host).acquireSync(weight));
177
+ return request('GET', SpotBaseURL, endpoint, params);
178
+ };
76
179
  //# sourceMappingURL=public-api.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"public-api.js","sourceRoot":"","sources":["../../src/api/public-api.ts"],"names":[],"mappings":"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","sourcesContent":["import { GlobalPrometheusRegistry, Terminal } from '@yuants/protocol';\n\nconst MetricsAsterApiCallCounter = GlobalPrometheusRegistry.counter(\n 'aster_api_call',\n 'Number of aster api call',\n);\nconst terminal = Terminal.fromNodeEnv();\nconst request = async <T>(\n method: string,\n baseUrl: string,\n endpoint: string,\n params: Record<string, unknown> = {},\n): Promise<T> => {\n const url = new URL(baseUrl);\n url.pathname = endpoint;\n for (const [key, value] of Object.entries(params)) {\n if (value === undefined) continue;\n url.searchParams.set(key, `${value}`);\n }\n\n console.info(url.toString());\n MetricsAsterApiCallCounter.labels({ path: url.pathname, terminal_id: terminal.terminal_id }).inc();\n const res = (await fetch(url.toString(), {\n method,\n }).then((response) => response.json())) as unknown;\n\n const maybeError = res as { code?: number };\n if (typeof maybeError.code === 'number' && maybeError.code !== 0) {\n throw JSON.stringify(res);\n }\n return res as T;\n};\n\nconst createApi =\n (baseUrl: string) =>\n <TReq extends Record<string, unknown>, TRes>(method: string, endpoint: string) =>\n (params: TReq) =>\n request<TRes>(method, baseUrl, endpoint, params);\n\nconst createFutureApi = createApi('https://fapi.asterdex.com');\nconst createSpotApi = createApi('https://sapi.asterdex.com');\n\n/**\n * 获取资金费率历史\n *\n * https://github.com/asterdex/api-docs/blob/master/aster-finance-futures-api_CN.md#%E6%9F%A5%E8%AF%A2%E8%B5%84%E9%87%91%E8%B4%B9%E7%8E%87%E5%8E%86%E5%8F%B2\n */\nexport const getFApiV1FundingRate = createFutureApi<\n {\n symbol?: string;\n startTime?: number;\n endTime?: number;\n limit?: number;\n },\n {\n symbol: string;\n fundingRate: string;\n fundingTime: number;\n }[]\n>('GET', '/fapi/v1/fundingRate');\n\nexport interface IAsterRateLimit {\n rateLimitType?: string;\n interval?: string;\n intervalNum?: number;\n limit?: number;\n}\n\nexport interface IAsterExchangeInfo {\n symbols: {\n symbol: string;\n status: 'TRADING' | 'BREAK' | 'HALT';\n baseAsset: string;\n quoteAsset: string;\n pricePrecision: number;\n quantityPrecision: number;\n baseAssetPrecision: number;\n quotePrecision: number;\n filters: {\n filterType: string;\n [key: string]: unknown;\n }[];\n }[];\n rateLimits?: IAsterRateLimit[];\n}\n\n/**\n * 获取交易对信息\n *\n * https://github.com/asterdex/api-docs/blob/master/aster-finance-futures-api_CN.md#%E4%BA%A4%E6%98%93%E5%AF%B9%E4%BF%A1%E6%81%AF\n */\nexport const getFApiV1ExchangeInfo = createFutureApi<Record<string, never>, IAsterExchangeInfo>(\n 'GET',\n '/fapi/v1/exchangeInfo',\n);\n\n/**\n * 获取现货交易对信息\n *\n * https://github.com/asterdex/api-docs/blob/master/aster-finance-spot-api_CN.md#L1080-L1145\n */\nexport const getApiV1ExchangeInfo = createSpotApi<Record<string, never>, IAsterExchangeInfo>(\n 'GET',\n '/api/v1/exchangeInfo',\n);\n\n/**\n * 获取未平仓合约数量\n *\n * 无 API 文档 (weird)\n */\nexport const getFApiV1OpenInterest = createFutureApi<\n {\n symbol: string;\n },\n {\n symbol: string;\n openInterest: string;\n time: number;\n }\n>('GET', '/fapi/v1/openInterest');\n\n/**\n * 获取最新价格\n *\n * https://github.com/asterdex/api-docs/blob/master/aster-finance-futures-api_CN.md#%E6%9C%80%E6%96%B0%E4%BB%B7%E6%A0%BC\n */\nexport const getFApiV1TickerPrice = createFutureApi<\n Record<string, never>,\n {\n symbol: string;\n price: string;\n time?: number;\n }[]\n>('GET', '/fapi/v1/ticker/price');\n\n/**\n * 获取资金费率\n * https://github.com/asterdex/api-docs/blob/master/aster-finance-futures-api_CN.md\n */\nexport const getFApiV1PremiumIndex = createFutureApi<\n {\n symbol?: string;\n },\n | {\n symbol: string; // 交易对\n markPrice: string; // 标记价格\n indexPrice: string; // 指数价格\n estimatedSettlePrice: string; // 预估结算价,仅在交割开始前最后一小时有意义\n lastFundingRate: string; // 最近更新的资金费率\n nextFundingTime: number; // 下次资金费时间\n interestRate: string; // 标的资产基础利率\n time: number; // 更新时间\n }\n | {\n symbol: string; // 交易对\n markPrice: string; // 标记价格\n indexPrice: string; // 指数价格\n estimatedSettlePrice: string; // 预估结算价,仅在交割开始前最后一小时有意义\n lastFundingRate: string; // 最近更新的资金费率\n nextFundingTime: number; // 下次资金费时间\n interestRate: string; // 标的资产基础利率\n time: number; // 更新时间\n }[]\n>('GET', '/fapi/v1/premiumIndex');\n\nexport interface IAsterKline extends Array<string | number> {\n 0: number; // Open time\n 1: string; // Open\n 2: string; // High\n 3: string; // Low\n 4: string; // Close\n 5: string; // Volume\n 6: number; // Close time\n 7: string; // Quote asset volume\n 8: number; // Number of trades\n 9: string; // Taker buy base asset volume\n 10: string; // Taker buy quote asset volume\n 11: string; // Ignore\n}\n\n/**\n * 获取 K 线\n *\n * 参考 Binance 风格接口:/fapi/v1/klines\n * https://github.com/asterdex/api-docs/blob/master/aster-finance-futures-api_CN.md#k%E7%BA%BF%E6%95%B0%E6%8D%AE\n */\nexport const getFApiV1Klines = createFutureApi<\n {\n symbol: string;\n interval: string;\n startTime?: number;\n endTime?: number;\n limit?: number;\n },\n IAsterKline[]\n>('GET', '/fapi/v1/klines');\n\n/**\n * 获取现货 K 线\n *\n * 参考 Binance 风格接口:/api/v1/klines\n *\n * https://github.com/asterdex/api-docs/blob/master/aster-finance-spot-api_CN.md\n */\nexport const getApiV1Klines = createSpotApi<\n {\n symbol: string;\n interval: string;\n startTime?: number;\n endTime?: number;\n limit?: number;\n },\n IAsterKline[]\n>('GET', '/api/v1/klines');\n"]}
1
+ {"version":3,"file":"public-api.js","sourceRoot":"","sources":["../../src/api/public-api.ts"],"names":[],"mappings":"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","sourcesContent":["import { GlobalPrometheusRegistry, Terminal } from '@yuants/protocol';\nimport { scopeError, tokenBucket } from '@yuants/utils';\n\nimport './client';\n\nconst MetricsAsterApiCallCounter = GlobalPrometheusRegistry.counter(\n 'aster_api_call',\n 'Number of aster api call',\n);\nconst terminal = Terminal.fromNodeEnv();\n\nconst FutureBaseURL = 'https://fapi.asterdex.com';\nconst SpotBaseURL = 'https://sapi.asterdex.com';\n\nconst getKlinesRequestWeight = (limit: number | undefined): number => {\n const resolvedLimit = limit ?? 500;\n if (resolvedLimit < 100) return 1;\n if (resolvedLimit < 500) return 2;\n if (resolvedLimit <= 1000) return 5;\n return 10;\n};\n\nconst request = async <T>(\n method: string,\n baseUrl: string,\n endpoint: string,\n params: Record<string, unknown> = {},\n): Promise<T> => {\n const url = new URL(baseUrl);\n url.pathname = endpoint;\n for (const [key, value] of Object.entries(params)) {\n if (value === undefined) continue;\n url.searchParams.set(key, `${value}`);\n }\n\n console.info(url.toString());\n MetricsAsterApiCallCounter.labels({ path: url.pathname, terminal_id: terminal.terminal_id }).inc();\n const res = (await fetch(url.toString(), {\n method,\n }).then((response) => response.json())) as unknown;\n\n const maybeError = res as { code?: number };\n if (typeof maybeError.code === 'number' && maybeError.code !== 0) {\n throw JSON.stringify(res);\n }\n return res as T;\n};\n\n/**\n * 获取资金费率历史\n *\n * Weight: 1\n *\n * https://github.com/asterdex/api-docs/blob/master/aster-finance-futures-api_CN.md#%E6%9F%A5%E8%AF%A2%E8%B5%84%E9%87%91%E8%B4%B9%E7%8E%87%E5%8E%86%E5%8F%B2\n */\nexport const getFApiV1FundingRate = (params: {\n symbol?: string;\n startTime?: number;\n endTime?: number;\n limit?: number;\n}): Promise<\n {\n symbol: string;\n fundingRate: string;\n fundingTime: number;\n }[]\n> => {\n const endpoint = '/fapi/v1/fundingRate';\n const url = new URL(FutureBaseURL);\n url.pathname = endpoint;\n const weight = 1;\n scopeError(\n 'ASTER_API_RATE_LIMIT',\n { method: 'GET', endpoint, host: url.host, path: url.pathname, bucketId: url.host, weight },\n () => tokenBucket(url.host).acquireSync(weight),\n );\n return request('GET', FutureBaseURL, endpoint, params);\n};\n\nexport interface IAsterRateLimit {\n rateLimitType?: string;\n interval?: string;\n intervalNum?: number;\n limit?: number;\n}\n\nexport interface IAsterExchangeInfo {\n symbols: {\n symbol: string;\n status: 'TRADING' | 'BREAK' | 'HALT';\n baseAsset: string;\n quoteAsset: string;\n pricePrecision: number;\n quantityPrecision: number;\n baseAssetPrecision: number;\n quotePrecision: number;\n filters: {\n filterType: string;\n [key: string]: unknown;\n }[];\n }[];\n rateLimits?: IAsterRateLimit[];\n}\n\n/**\n * 获取交易对信息\n *\n * Weight: 1\n *\n * https://github.com/asterdex/api-docs/blob/master/aster-finance-futures-api_CN.md#%E4%BA%A4%E6%98%93%E5%AF%B9%E4%BF%A1%E6%81%AF\n */\nexport const getFApiV1ExchangeInfo = (params: Record<string, never>): Promise<IAsterExchangeInfo> => {\n const endpoint = '/fapi/v1/exchangeInfo';\n const url = new URL(FutureBaseURL);\n url.pathname = endpoint;\n const weight = 1;\n scopeError(\n 'ASTER_API_RATE_LIMIT',\n { method: 'GET', endpoint, host: url.host, path: url.pathname, bucketId: url.host, weight },\n () => tokenBucket(url.host).acquireSync(weight),\n );\n return request('GET', FutureBaseURL, endpoint, params);\n};\n\n/**\n * 获取现货交易对信息\n *\n * Weight: 1\n *\n * https://github.com/asterdex/api-docs/blob/master/aster-finance-spot-api_CN.md#L1080-L1145\n */\nexport const getApiV1ExchangeInfo = (params: Record<string, never>): Promise<IAsterExchangeInfo> => {\n const endpoint = '/api/v1/exchangeInfo';\n const url = new URL(SpotBaseURL);\n url.pathname = endpoint;\n const weight = 1;\n scopeError(\n 'ASTER_API_RATE_LIMIT',\n { method: 'GET', endpoint, host: url.host, path: url.pathname, bucketId: url.host, weight },\n () => tokenBucket(url.host).acquireSync(weight),\n );\n return request('GET', SpotBaseURL, endpoint, params);\n};\n\n/**\n * 获取未平仓合约数量\n *\n * 无 API 文档 (weird)\n * 参考 Binance 风格接口:/fapi/v1/openInterest\n *\n * Weight: 1\n * https://developers.binance.com/docs/zh-CN/derivatives/usds-margined-futures/market-data/rest-api/Open-Interest\n */\nexport const getFApiV1OpenInterest = (params: {\n symbol: string;\n}): Promise<{\n symbol: string;\n openInterest: string;\n time: number;\n}> => {\n const endpoint = '/fapi/v1/openInterest';\n const url = new URL(FutureBaseURL);\n url.pathname = endpoint;\n const weight = 1;\n scopeError(\n 'ASTER_API_RATE_LIMIT',\n { method: 'GET', endpoint, host: url.host, path: url.pathname, bucketId: url.host, weight },\n () => tokenBucket(url.host).acquireSync(weight),\n );\n return request('GET', FutureBaseURL, endpoint, params);\n};\n\n/**\n * 获取最新价格\n *\n * Weight: without symbol 2 (current implementation)\n *\n * https://github.com/asterdex/api-docs/blob/master/aster-finance-futures-api_CN.md#%E6%9C%80%E6%96%B0%E4%BB%B7%E6%A0%BC\n */\nexport const getFApiV1TickerPrice = (\n params: Record<string, never>,\n): Promise<\n {\n symbol: string;\n price: string;\n time?: number;\n }[]\n> => {\n const endpoint = '/fapi/v1/ticker/price';\n const url = new URL(FutureBaseURL);\n url.pathname = endpoint;\n const weight = 2;\n scopeError(\n 'ASTER_API_RATE_LIMIT',\n { method: 'GET', endpoint, host: url.host, path: url.pathname, bucketId: url.host, weight },\n () => tokenBucket(url.host).acquireSync(weight),\n );\n return request('GET', FutureBaseURL, endpoint, params);\n};\n\n/**\n * 获取资金费率\n *\n * Weight: 1\n *\n * https://github.com/asterdex/api-docs/blob/master/aster-finance-futures-api_CN.md\n */\nexport const getFApiV1PremiumIndex = (params: {\n symbol?: string;\n}): Promise<\n | {\n symbol: string;\n markPrice: string;\n indexPrice: string;\n estimatedSettlePrice: string;\n lastFundingRate: string;\n nextFundingTime: number;\n interestRate: string;\n time: number;\n }\n | {\n symbol: string;\n markPrice: string;\n indexPrice: string;\n estimatedSettlePrice: string;\n lastFundingRate: string;\n nextFundingTime: number;\n interestRate: string;\n time: number;\n }[]\n> => {\n const endpoint = '/fapi/v1/premiumIndex';\n const url = new URL(FutureBaseURL);\n url.pathname = endpoint;\n const weight = 1;\n scopeError(\n 'ASTER_API_RATE_LIMIT',\n { method: 'GET', endpoint, host: url.host, path: url.pathname, bucketId: url.host, weight },\n () => tokenBucket(url.host).acquireSync(weight),\n );\n return request('GET', FutureBaseURL, endpoint, params);\n};\n\nexport interface IAsterKline extends Array<string | number> {\n 0: number; // Open time\n 1: string; // Open\n 2: string; // High\n 3: string; // Low\n 4: string; // Close\n 5: string; // Volume\n 6: number; // Close time\n 7: string; // Quote asset volume\n 8: number; // Number of trades\n 9: string; // Taker buy base asset volume\n 10: string; // Taker buy quote asset volume\n 11: string; // Ignore\n}\n\n/**\n * 获取 K 线\n *\n * 参考 Binance 风格接口:/fapi/v1/klines\n *\n * Weight: by limit\n *\n * https://github.com/asterdex/api-docs/blob/master/aster-finance-futures-api_CN.md#k%E7%BA%BF%E6%95%B0%E6%8D%AE\n */\nexport const getFApiV1Klines = (params: {\n symbol: string;\n interval: string;\n startTime?: number;\n endTime?: number;\n limit?: number;\n}): Promise<IAsterKline[]> => {\n const endpoint = '/fapi/v1/klines';\n const url = new URL(FutureBaseURL);\n url.pathname = endpoint;\n const weight = getKlinesRequestWeight(params?.limit);\n scopeError(\n 'ASTER_API_RATE_LIMIT',\n {\n method: 'GET',\n endpoint,\n host: url.host,\n path: url.pathname,\n bucketId: url.host,\n weight,\n limit: params?.limit,\n },\n () => tokenBucket(url.host).acquireSync(weight),\n );\n return request('GET', FutureBaseURL, endpoint, params);\n};\n\n/**\n * 获取现货 K 线\n *\n * 参考 Binance 风格接口:/api/v1/klines\n *\n * Weight: not documented (temporary: follow futures limit table)\n *\n * https://github.com/asterdex/api-docs/blob/master/aster-finance-spot-api_CN.md\n */\nexport const getApiV1Klines = (params: {\n symbol: string;\n interval: string;\n startTime?: number;\n endTime?: number;\n limit?: number;\n}): Promise<IAsterKline[]> => {\n const endpoint = '/api/v1/klines';\n const url = new URL(SpotBaseURL);\n url.pathname = endpoint;\n const weight = getKlinesRequestWeight(params?.limit);\n scopeError(\n 'ASTER_API_RATE_LIMIT',\n {\n method: 'GET',\n endpoint,\n host: url.host,\n path: url.pathname,\n bucketId: url.host,\n weight,\n limit: params?.limit,\n },\n () => tokenBucket(url.host).acquireSync(weight),\n );\n return request('GET', SpotBaseURL, endpoint, params);\n};\n"]}
@@ -6,6 +6,7 @@ import { decodePath, encodePath } from '@yuants/utils';
6
6
  import { catchError, combineLatest, concatMap, defer, exhaustMap, filter, from, groupBy, map, merge, mergeMap, of, repeat, retry, scan, shareReplay, startWith, timer, } from 'rxjs';
7
7
  import { getFApiV1ExchangeInfo, getFApiV1OpenInterest, getFApiV1PremiumIndex, getFApiV1TickerPrice, } from '../../api/public-api';
8
8
  const terminal = Terminal.fromNodeEnv();
9
+ const DISABLE_OPEN_INTEREST = process.env.DISABLE_OPEN_INTEREST === 'true';
9
10
  const DEFAULT_OPEN_INTEREST_REQUEST_INTERVAL_MS = 500;
10
11
  const OPEN_INTEREST_TTL = process.env.OPEN_INTEREST_TTL ? Number(process.env.OPEN_INTEREST_TTL) : 120000;
11
12
  const toIntervalMs = (interval, intervalNum) => {
@@ -96,7 +97,7 @@ const quoteFromFundingRate$ = fundingRate$.pipe(mergeMap((premiumDataArray) => p
96
97
  interest_rate_long: premiumData.lastFundingRate ? `${-+premiumData.lastFundingRate}` : undefined,
97
98
  interest_rate_short: premiumData.lastFundingRate,
98
99
  })));
99
- const quote$ = merge(quoteFromTicker$, quoteFromOpenInterest$, quoteFromFundingRate$).pipe(groupBy((quote) => quote.product_id), mergeMap((group$) => group$.pipe(scan((acc, cur) => Object.assign(acc, cur, {
100
+ const quote$ = merge(quoteFromTicker$, quoteFromFundingRate$, DISABLE_OPEN_INTEREST ? of() : quoteFromOpenInterest$).pipe(groupBy((quote) => quote.product_id), mergeMap((group$) => group$.pipe(scan((acc, cur) => Object.assign(acc, cur, {
100
101
  datasource_id: 'ASTER',
101
102
  product_id: group$.key,
102
103
  }), {}))), shareReplay({ bufferSize: 1, refCount: true }));