@imbingox/acex 0.1.0 → 0.2.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 (85) hide show
  1. package/README.md +92 -285
  2. package/index.ts +1 -0
  3. package/package.json +40 -23
  4. package/src/adapters/binance/adapter.ts +80 -0
  5. package/src/adapters/binance/book-ticker.ts +123 -0
  6. package/src/adapters/binance/mark-price.ts +126 -0
  7. package/src/adapters/binance/market-catalog.ts +258 -0
  8. package/src/adapters/binance/private-adapter.ts +833 -0
  9. package/src/adapters/types.ts +219 -0
  10. package/src/client/context.ts +123 -0
  11. package/src/client/create-client.ts +6 -0
  12. package/src/client/private-subscription-coordinator.ts +512 -0
  13. package/src/client/runtime.ts +410 -0
  14. package/src/errors.ts +27 -0
  15. package/src/index.ts +5 -0
  16. package/src/internal/async-event-bus.ts +100 -0
  17. package/src/internal/filters.ts +117 -0
  18. package/src/internal/managed-websocket.ts +280 -0
  19. package/src/managers/account-manager.ts +609 -0
  20. package/src/managers/market-manager.ts +889 -0
  21. package/src/managers/order-manager.ts +685 -0
  22. package/src/types/account.ts +157 -0
  23. package/src/types/client.ts +79 -0
  24. package/src/types/index.ts +5 -0
  25. package/src/types/market.ts +150 -0
  26. package/src/types/order.ts +177 -0
  27. package/src/types/shared.ts +93 -0
  28. package/dist/adapters/binance/composite-adapter.d.ts +0 -116
  29. package/dist/adapters/binance/composite-adapter.js +0 -121
  30. package/dist/adapters/binance/market-types.d.ts +0 -63
  31. package/dist/adapters/binance/market-types.js +0 -1
  32. package/dist/adapters/binance/native-market-adapter.d.ts +0 -102
  33. package/dist/adapters/binance/native-market-adapter.js +0 -455
  34. package/dist/adapters/binance/normalizers.d.ts +0 -8
  35. package/dist/adapters/binance/normalizers.js +0 -123
  36. package/dist/adapters/binance/rest-client.d.ts +0 -17
  37. package/dist/adapters/binance/rest-client.js +0 -66
  38. package/dist/adapters/binance/symbol-router.d.ts +0 -9
  39. package/dist/adapters/binance/symbol-router.js +0 -174
  40. package/dist/adapters/binance/ws-client.d.ts +0 -24
  41. package/dist/adapters/binance/ws-client.js +0 -261
  42. package/dist/adapters/ccxt/aster-ccxt-adapter.d.ts +0 -157
  43. package/dist/adapters/ccxt/aster-ccxt-adapter.js +0 -272
  44. package/dist/adapters/ccxt/binance-usdm-ccxt-adapter.d.ts +0 -180
  45. package/dist/adapters/ccxt/binance-usdm-ccxt-adapter.js +0 -539
  46. package/dist/adapters/ccxt/binance-usdm-exchange.d.ts +0 -22
  47. package/dist/adapters/ccxt/binance-usdm-exchange.js +0 -23
  48. package/dist/adapters/fake/fake-aster-adapter.d.ts +0 -130
  49. package/dist/adapters/fake/fake-aster-adapter.js +0 -283
  50. package/dist/adapters/types.d.ts +0 -210
  51. package/dist/adapters/types.js +0 -1
  52. package/dist/core/client.d.ts +0 -50
  53. package/dist/core/client.js +0 -403
  54. package/dist/core/recovery.d.ts +0 -22
  55. package/dist/core/recovery.js +0 -18
  56. package/dist/core/runtime.d.ts +0 -26
  57. package/dist/core/runtime.js +0 -150
  58. package/dist/errors/acex-error.d.ts +0 -25
  59. package/dist/errors/acex-error.js +0 -54
  60. package/dist/index.d.ts +0 -6
  61. package/dist/index.js +0 -3
  62. package/dist/managers/account-manager.d.ts +0 -41
  63. package/dist/managers/account-manager.js +0 -80
  64. package/dist/managers/market-manager.d.ts +0 -16
  65. package/dist/managers/market-manager.js +0 -28
  66. package/dist/managers/order-manager.d.ts +0 -87
  67. package/dist/managers/order-manager.js +0 -122
  68. package/dist/runtime/async-queue.d.ts +0 -8
  69. package/dist/runtime/async-queue.js +0 -88
  70. package/dist/runtime/request-id.d.ts +0 -1
  71. package/dist/runtime/request-id.js +0 -5
  72. package/dist/runtime/ws-connection-supervisor.d.ts +0 -76
  73. package/dist/runtime/ws-connection-supervisor.js +0 -522
  74. package/dist/store/account-store.d.ts +0 -52
  75. package/dist/store/account-store.js +0 -18
  76. package/dist/store/health-store.d.ts +0 -16
  77. package/dist/store/health-store.js +0 -29
  78. package/dist/store/market-store.d.ts +0 -42
  79. package/dist/store/market-store.js +0 -51
  80. package/dist/store/order-store.d.ts +0 -38
  81. package/dist/store/order-store.js +0 -49
  82. package/dist/testing/create-fake-runtime.d.ts +0 -5
  83. package/dist/testing/create-fake-runtime.js +0 -7
  84. package/dist/types/public.d.ts +0 -5
  85. package/dist/types/public.js +0 -1
@@ -1,455 +0,0 @@
1
- import { createAcexError } from "../../errors/acex-error.js";
2
- import { createAsyncQueue } from "../../runtime/async-queue.js";
3
- import { normalizeBookTickerEvent, normalizeMarkPriceEvent } from "./normalizers.js";
4
- import { createBinanceMarketRestClient } from "./rest-client.js";
5
- import { buildBinanceMarketIndex, getRecordByNativeSymbol, requireFundingMarketRecord, requireMarketRecord, } from "./symbol-router.js";
6
- import { createBinanceMarketWsTransport, } from "./ws-client.js";
7
- function createUnsupportedError(message) {
8
- return createAcexError({
9
- code: "CAPABILITY_NOT_SUPPORTED",
10
- message,
11
- retryable: false,
12
- exchange: "binance",
13
- });
14
- }
15
- function createAdapterLifecycleError(message) {
16
- return createAcexError({
17
- code: "VALIDATION_ERROR",
18
- message,
19
- retryable: false,
20
- exchange: "binance",
21
- });
22
- }
23
- function toError(error) {
24
- return error instanceof Error ? error : new Error(String(error));
25
- }
26
- function subscriptionKey(subscription) {
27
- return `${subscription.family}:${subscription.nativeSymbol}:${subscription.streamKind}`;
28
- }
29
- function marketTypeFor(record) {
30
- if (record.family === "spot") {
31
- return "spot";
32
- }
33
- return record.fundingEligible ? "swap" : "future";
34
- }
35
- function createSymbolEventKey(event) {
36
- return `${event.family}:${event.nativeSymbol}`;
37
- }
38
- export class BinanceNativeMarketAdapter {
39
- exchange = "binance";
40
- capabilities = {
41
- publicWs: true,
42
- privateWs: false,
43
- l1BookStream: true,
44
- fundingRateStream: true,
45
- accountStream: false,
46
- orderStream: false,
47
- fetchMarketInfo: true,
48
- fetchBalances: false,
49
- fetchPositions: false,
50
- fetchRisk: false,
51
- fetchOpenOrders: false,
52
- fetchMyTrades: false,
53
- fetchOrderById: false,
54
- };
55
- #restClient;
56
- #createTransport;
57
- #now;
58
- #started = false;
59
- #stopping = false;
60
- #stopTask;
61
- #marketIndex;
62
- #marketInfo = [];
63
- #transports = new Map();
64
- #transportConnections = new Map();
65
- #transportTasks = new Map();
66
- #reconnectTasks = new Map();
67
- #subscriptions = new Map();
68
- #subscriptionTasks = new Map();
69
- #marketQueue = createAsyncQueue({ maxBufferSize: 100 });
70
- #marketEventSink;
71
- #internalErrorQueue = createAsyncQueue({ maxBufferSize: 20 });
72
- #orderQueue = createAsyncQueue({ maxBufferSize: 10 });
73
- #orderEventSink;
74
- #accountQueue = createAsyncQueue({ maxBufferSize: 10 });
75
- #accountEventSink;
76
- constructor(input = {}) {
77
- this.#restClient = input.restClient ?? createBinanceMarketRestClient();
78
- this.#createTransport =
79
- input.createTransport ??
80
- ((transportInput) => createBinanceMarketWsTransport({
81
- family: transportInput.family,
82
- createSocket: (url) => new WebSocket(url),
83
- onMessage: (payload) => {
84
- this.handleTransportMessage(transportInput.family, payload);
85
- },
86
- onClose: () => {
87
- this.handleTransportClose(transportInput.family);
88
- },
89
- onReconnect: async () => {
90
- await this.handleTransportReconnect(transportInput.family);
91
- },
92
- }));
93
- this.#now = input.now ?? (() => Date.now());
94
- }
95
- async start() {
96
- if (this.#stopping || this.#stopTask !== undefined) {
97
- throw createAdapterLifecycleError("market adapter cannot be started after stop begins");
98
- }
99
- if (this.#started) {
100
- return;
101
- }
102
- this.#marketIndex = buildBinanceMarketIndex(await this.#restClient.fetchAllExchangeInfo());
103
- this.#marketInfo = this.#marketIndex.markets.map((record) => ({
104
- exchange: "binance",
105
- symbol: record.unifiedSymbol,
106
- baseAsset: record.baseAsset,
107
- quoteAsset: record.quoteAsset,
108
- marketType: marketTypeFor(record),
109
- pricePrecision: record.pricePrecision,
110
- amountPrecision: record.amountPrecision,
111
- active: record.active,
112
- }));
113
- this.#started = true;
114
- }
115
- async stop() {
116
- if (this.#stopTask !== undefined) {
117
- await this.#stopTask;
118
- return;
119
- }
120
- this.#stopTask = (async () => {
121
- this.#stopping = true;
122
- this.#started = false;
123
- await Promise.allSettled([...this.#transports.values()].map((transport) => transport.close()));
124
- await Promise.allSettled([
125
- ...this.#transportTasks.values(),
126
- ...this.#subscriptionTasks.values(),
127
- ...this.#reconnectTasks.values(),
128
- ]);
129
- this.#transports.clear();
130
- this.#transportConnections.clear();
131
- this.#transportTasks.clear();
132
- this.#reconnectTasks.clear();
133
- this.#subscriptions.clear();
134
- this.#subscriptionTasks.clear();
135
- this.#marketIndex = undefined;
136
- this.#marketInfo = [];
137
- this.#marketQueue.close();
138
- this.#internalErrorQueue.close();
139
- this.#orderQueue.close();
140
- this.#accountQueue.close();
141
- })();
142
- try {
143
- await this.#stopTask;
144
- }
145
- finally {
146
- this.#stopTask = undefined;
147
- }
148
- }
149
- async fetchMarketInfo() {
150
- this.#requireMarketIndex();
151
- return this.#marketInfo.map((market) => ({ ...market }));
152
- }
153
- async subscribeL1Book(input) {
154
- const marketIndex = this.#requireMarketIndex();
155
- const record = requireMarketRecord(marketIndex, input.symbol);
156
- await this.#ensureSubscribed(record, "l1Book", `${record.nativeSymbol.toLowerCase()}@bookTicker`);
157
- }
158
- async subscribeFundingRate(input) {
159
- const marketIndex = this.#requireMarketIndex();
160
- const record = requireFundingMarketRecord(marketIndex, input.symbol);
161
- await this.#ensureSubscribed(record, "fundingRate", `${record.nativeSymbol.toLowerCase()}@markPrice@1s`);
162
- }
163
- watchMarketEvents() {
164
- return this.#marketQueue;
165
- }
166
- setMarketEventSink(sink) {
167
- this.#marketEventSink = sink;
168
- }
169
- watchInternalErrors() {
170
- return this.#internalErrorQueue;
171
- }
172
- async subscribeOrders(_input) {
173
- throw createUnsupportedError("private orders stay on ccxt adapter");
174
- }
175
- async fetchOpenOrdersBaseline(_accountId) {
176
- throw createUnsupportedError("private orders stay on ccxt adapter");
177
- }
178
- watchOrderEvents() {
179
- return this.#orderQueue;
180
- }
181
- setOrderEventSink(sink) {
182
- this.#orderEventSink = sink;
183
- }
184
- async fetchAccountBaseline(_accountId) {
185
- return {
186
- balances: {},
187
- positions: [],
188
- };
189
- }
190
- watchAccountEvents() {
191
- return this.#accountQueue;
192
- }
193
- setAccountEventSink(sink) {
194
- this.#accountEventSink = sink;
195
- }
196
- async placeOrder(_input) {
197
- throw createUnsupportedError("market adapter does not place orders");
198
- }
199
- async amendOrder(_input) {
200
- throw createUnsupportedError("market adapter does not amend orders");
201
- }
202
- async cancelOrder(_input) {
203
- throw createUnsupportedError("market adapter does not cancel orders");
204
- }
205
- async cancelAllOrders(_input) {
206
- throw createUnsupportedError("market adapter does not cancel all orders");
207
- }
208
- getHealth() {
209
- if (!this.#started) {
210
- return {
211
- exchange: this.exchange,
212
- status: "idle",
213
- wsConnected: false,
214
- };
215
- }
216
- const subscribedFamilies = new Set();
217
- for (const subscription of this.#subscriptions.values()) {
218
- subscribedFamilies.add(subscription.family);
219
- }
220
- if (subscribedFamilies.size === 0) {
221
- return {
222
- exchange: this.exchange,
223
- status: "healthy",
224
- wsConnected: true,
225
- };
226
- }
227
- const allFamiliesConnected = [...subscribedFamilies].every((family) => this.#transportConnections.get(family) === true);
228
- return {
229
- exchange: this.exchange,
230
- status: allFamiliesConnected ? "healthy" : "degraded",
231
- wsConnected: allFamiliesConnected,
232
- };
233
- }
234
- handleTransportMessage(family, payload) {
235
- if (this.#stopping || this.#marketIndex === undefined) {
236
- return;
237
- }
238
- const nativeSymbol = this.#readPayloadSymbol(payload);
239
- if (nativeSymbol === undefined) {
240
- return;
241
- }
242
- const record = getRecordByNativeSymbol(this.#marketIndex, family, nativeSymbol);
243
- if (record === undefined) {
244
- return;
245
- }
246
- try {
247
- const receivedAt = this.#now();
248
- const event = this.#isFundingPayload(payload)
249
- ? normalizeMarkPriceEvent(record, payload, receivedAt)
250
- : normalizeBookTickerEvent(record, payload, receivedAt);
251
- this.#emitMarketEvent(event);
252
- }
253
- catch (error) {
254
- this.#emitInternalError(error);
255
- }
256
- }
257
- handleTransportClose(family) {
258
- if (this.#stopping || this.#marketIndex === undefined) {
259
- return;
260
- }
261
- this.#transportConnections.set(family, false);
262
- const receivedAt = this.#now();
263
- const emittedSymbols = new Set();
264
- for (const subscription of this.#subscriptions.values()) {
265
- if (subscription.family !== family) {
266
- continue;
267
- }
268
- const symbolEventKey = createSymbolEventKey(subscription);
269
- if (emittedSymbols.has(symbolEventKey)) {
270
- continue;
271
- }
272
- const record = getRecordByNativeSymbol(this.#marketIndex, family, subscription.nativeSymbol);
273
- if (record === undefined) {
274
- continue;
275
- }
276
- emittedSymbols.add(symbolEventKey);
277
- this.#emitMarketEvent({
278
- type: "market.disconnected",
279
- exchange: "binance",
280
- symbol: record.unifiedSymbol,
281
- receivedAt,
282
- });
283
- }
284
- }
285
- async handleTransportReconnect(family) {
286
- if (this.#stopping || !this.#started) {
287
- return;
288
- }
289
- const existingTask = this.#reconnectTasks.get(family);
290
- if (existingTask !== undefined) {
291
- await existingTask;
292
- return;
293
- }
294
- const task = this.#runTransportReconnect(family);
295
- this.#reconnectTasks.set(family, task);
296
- try {
297
- await task;
298
- }
299
- finally {
300
- this.#reconnectTasks.delete(family);
301
- }
302
- }
303
- async #runTransportReconnect(family) {
304
- if (this.#stopping || this.#marketIndex === undefined) {
305
- return;
306
- }
307
- const transport = this.#transports.get(family);
308
- if (transport === undefined) {
309
- return;
310
- }
311
- const receivedAt = this.#now();
312
- const emittedSymbols = new Set();
313
- let reconnectSucceeded = true;
314
- for (const subscription of this.#subscriptions.values()) {
315
- if (this.#stopping) {
316
- return;
317
- }
318
- if (subscription.family !== family) {
319
- continue;
320
- }
321
- const record = getRecordByNativeSymbol(this.#marketIndex, family, subscription.nativeSymbol);
322
- if (record !== undefined) {
323
- const symbolEventKey = createSymbolEventKey(subscription);
324
- if (!emittedSymbols.has(symbolEventKey)) {
325
- emittedSymbols.add(symbolEventKey);
326
- this.#emitMarketEvent({
327
- type: "market.reconnecting",
328
- exchange: "binance",
329
- symbol: record.unifiedSymbol,
330
- receivedAt,
331
- });
332
- }
333
- }
334
- try {
335
- await transport.ensureSubscribed(subscription.streamName);
336
- }
337
- catch (error) {
338
- reconnectSucceeded = false;
339
- this.#emitInternalError(error);
340
- }
341
- }
342
- this.#transportConnections.set(family, reconnectSucceeded);
343
- }
344
- async #ensureSubscribed(record, streamKind, streamName) {
345
- if (this.#stopping) {
346
- throw createAdapterLifecycleError("market adapter must be started before using market methods");
347
- }
348
- const subscription = {
349
- family: record.family,
350
- nativeSymbol: record.nativeSymbol,
351
- streamKind,
352
- streamName,
353
- };
354
- const key = subscriptionKey(subscription);
355
- if (this.#subscriptions.has(key)) {
356
- return;
357
- }
358
- const existingTask = this.#subscriptionTasks.get(key);
359
- if (existingTask !== undefined) {
360
- await existingTask;
361
- return;
362
- }
363
- const task = (async () => {
364
- this.#subscriptions.set(key, subscription);
365
- try {
366
- const transport = await this.#getOrCreateTransport(record.family);
367
- if (this.#stopping) {
368
- throw createAdapterLifecycleError("market adapter must be started before using market methods");
369
- }
370
- await transport.ensureSubscribed(streamName);
371
- }
372
- catch (error) {
373
- this.#subscriptions.delete(key);
374
- throw error;
375
- }
376
- })();
377
- this.#subscriptionTasks.set(key, task);
378
- try {
379
- await task;
380
- }
381
- finally {
382
- this.#subscriptionTasks.delete(key);
383
- }
384
- }
385
- async #getOrCreateTransport(family) {
386
- if (this.#stopping) {
387
- throw createAdapterLifecycleError("market adapter must be started before using market methods");
388
- }
389
- const existing = this.#transports.get(family);
390
- if (existing !== undefined) {
391
- return existing;
392
- }
393
- const existingTask = this.#transportTasks.get(family);
394
- if (existingTask !== undefined) {
395
- return await existingTask;
396
- }
397
- const task = (async () => {
398
- const transport = this.#createTransport({ family });
399
- this.#transports.set(family, transport);
400
- try {
401
- await transport.connect();
402
- if (this.#stopping) {
403
- await transport.close();
404
- throw createAdapterLifecycleError("market adapter must be started before using market methods");
405
- }
406
- this.#transportConnections.set(family, true);
407
- return transport;
408
- }
409
- catch (error) {
410
- this.#transports.delete(family);
411
- this.#transportConnections.delete(family);
412
- throw error;
413
- }
414
- })();
415
- this.#transportTasks.set(family, task);
416
- try {
417
- return await task;
418
- }
419
- finally {
420
- this.#transportTasks.delete(family);
421
- }
422
- }
423
- #requireMarketIndex() {
424
- if (!this.#started || this.#marketIndex === undefined) {
425
- throw createAdapterLifecycleError("market adapter must be started before using market methods");
426
- }
427
- return this.#marketIndex;
428
- }
429
- #readPayloadSymbol(payload) {
430
- if (typeof payload !== "object" || payload === null || !("s" in payload)) {
431
- return undefined;
432
- }
433
- const symbol = payload.s;
434
- return typeof symbol === "string" ? symbol : undefined;
435
- }
436
- #isFundingPayload(payload) {
437
- return typeof payload === "object" && payload !== null && "r" in payload;
438
- }
439
- #emitMarketEvent(event) {
440
- if (this.#stopping) {
441
- return;
442
- }
443
- if (this.#marketEventSink !== undefined) {
444
- this.#marketEventSink(event);
445
- return;
446
- }
447
- this.#marketQueue.push(event);
448
- }
449
- #emitInternalError(error) {
450
- if (this.#stopping) {
451
- return;
452
- }
453
- this.#internalErrorQueue.push(toError(error));
454
- }
455
- }
@@ -1,8 +0,0 @@
1
- import type { NormalizedMarketEvent } from "../types.js";
2
- import type { BinanceMarketRecord } from "./market-types.js";
3
- export declare function normalizeBookTickerEvent(record: BinanceMarketRecord, payload: unknown, receivedAt: number): Extract<NormalizedMarketEvent, {
4
- type: "l1_book.updated";
5
- }>;
6
- export declare function normalizeMarkPriceEvent(record: BinanceMarketRecord, payload: unknown, receivedAt: number): Extract<NormalizedMarketEvent, {
7
- type: "funding_rate.updated";
8
- }>;
@@ -1,123 +0,0 @@
1
- import { createAcexError } from "../../errors/acex-error.js";
2
- function createPayloadShapeError(stream, message) {
3
- return createAcexError({
4
- code: "VALIDATION_ERROR",
5
- message: `invalid binance ${stream} payload: ${message}`,
6
- retryable: false,
7
- exchange: "binance",
8
- });
9
- }
10
- function createPayloadValidationError(stream, record, payloadSymbol) {
11
- return createAcexError({
12
- code: "VALIDATION_ERROR",
13
- message: `invalid binance ${stream} payload symbol: expected ${record.nativeSymbol}, received ${payloadSymbol}`,
14
- retryable: false,
15
- exchange: "binance",
16
- });
17
- }
18
- function assertNativeSymbolMatches(stream, record, payloadSymbol) {
19
- if (payloadSymbol !== record.nativeSymbol) {
20
- throw createPayloadValidationError(stream, record, payloadSymbol);
21
- }
22
- }
23
- function assertFundingEligible(record) {
24
- if (!record.fundingEligible) {
25
- throw createAcexError({
26
- code: "CAPABILITY_NOT_SUPPORTED",
27
- message: "funding rate is not supported for spot or delivery markets",
28
- retryable: false,
29
- exchange: "binance",
30
- });
31
- }
32
- }
33
- function asPayloadRecord(stream, payload) {
34
- if (typeof payload !== "object" || payload === null) {
35
- throw createPayloadShapeError(stream, "payload must be an object");
36
- }
37
- return payload;
38
- }
39
- function readRequiredStringField(stream, payload, field) {
40
- const value = payload[field];
41
- if (typeof value !== "string" || value.length === 0) {
42
- throw createPayloadShapeError(stream, `missing or invalid ${field}`);
43
- }
44
- return value;
45
- }
46
- function readRequiredFiniteNumberField(stream, payload, field) {
47
- const value = payload[field];
48
- if (typeof value !== "number" || !Number.isFinite(value)) {
49
- throw createPayloadShapeError(stream, `missing or invalid ${field}`);
50
- }
51
- return value;
52
- }
53
- function readOptionalFiniteNumberField(stream, payload, field) {
54
- const value = payload[field];
55
- if (value === undefined) {
56
- return undefined;
57
- }
58
- if (typeof value !== "number" || !Number.isFinite(value)) {
59
- throw createPayloadShapeError(stream, `missing or invalid ${field}`);
60
- }
61
- return value;
62
- }
63
- function parseBookTickerPayload(record, payload) {
64
- const parsed = asPayloadRecord("bookTicker", payload);
65
- const symbol = readRequiredStringField("bookTicker", parsed, "s");
66
- const bidPrice = readRequiredStringField("bookTicker", parsed, "b");
67
- const bidSize = readRequiredStringField("bookTicker", parsed, "B");
68
- const askPrice = readRequiredStringField("bookTicker", parsed, "a");
69
- const askSize = readRequiredStringField("bookTicker", parsed, "A");
70
- const exchangeTs = readOptionalFiniteNumberField("bookTicker", parsed, "E");
71
- if (record.family === "spot") {
72
- if (exchangeTs !== undefined) {
73
- throw createPayloadShapeError("bookTicker", "spot bookTicker payload must not include E");
74
- }
75
- }
76
- else if (exchangeTs === undefined) {
77
- throw createPayloadShapeError("bookTicker", `${record.family} bookTicker payload must include E`);
78
- }
79
- return {
80
- s: symbol,
81
- b: bidPrice,
82
- B: bidSize,
83
- a: askPrice,
84
- A: askSize,
85
- ...(exchangeTs === undefined ? {} : { E: exchangeTs }),
86
- };
87
- }
88
- function parseMarkPricePayload(payload) {
89
- const parsed = asPayloadRecord("markPrice", payload);
90
- return {
91
- s: readRequiredStringField("markPrice", parsed, "s"),
92
- r: readRequiredStringField("markPrice", parsed, "r"),
93
- E: readRequiredFiniteNumberField("markPrice", parsed, "E"),
94
- };
95
- }
96
- export function normalizeBookTickerEvent(record, payload, receivedAt) {
97
- const parsed = parseBookTickerPayload(record, payload);
98
- assertNativeSymbolMatches("bookTicker", record, parsed.s);
99
- return {
100
- type: "l1_book.updated",
101
- exchange: "binance",
102
- symbol: record.unifiedSymbol,
103
- bidPrice: parsed.b,
104
- bidSize: parsed.B,
105
- askPrice: parsed.a,
106
- askSize: parsed.A,
107
- ...(parsed.E === undefined ? {} : { exchangeTs: parsed.E }),
108
- receivedAt,
109
- };
110
- }
111
- export function normalizeMarkPriceEvent(record, payload, receivedAt) {
112
- assertFundingEligible(record);
113
- const parsed = parseMarkPricePayload(payload);
114
- assertNativeSymbolMatches("markPrice", record, parsed.s);
115
- return {
116
- type: "funding_rate.updated",
117
- exchange: "binance",
118
- symbol: record.unifiedSymbol,
119
- fundingRate: parsed.r,
120
- exchangeTs: parsed.E,
121
- receivedAt,
122
- };
123
- }
@@ -1,17 +0,0 @@
1
- import type { BinanceCoinmExchangeInfo, BinanceSpotExchangeInfo, BinanceUsdmExchangeInfo } from "./market-types.js";
2
- export interface BinanceMarketRestClient {
3
- fetchAllExchangeInfo(): Promise<{
4
- spot: BinanceSpotExchangeInfo;
5
- usdm: BinanceUsdmExchangeInfo;
6
- coinm: BinanceCoinmExchangeInfo;
7
- }>;
8
- }
9
- export interface CreateBinanceMarketRestClientInput {
10
- fetchImpl?: typeof fetch;
11
- baseUrls?: {
12
- spot: string;
13
- usdm: string;
14
- coinm: string;
15
- };
16
- }
17
- export declare function createBinanceMarketRestClient(input?: CreateBinanceMarketRestClientInput): BinanceMarketRestClient;
@@ -1,66 +0,0 @@
1
- import { createAcexError } from "../../errors/acex-error.js";
2
- const DEFAULT_BASE_URLS = {
3
- spot: "https://api.binance.com",
4
- usdm: "https://fapi.binance.com",
5
- coinm: "https://dapi.binance.com",
6
- };
7
- function createTransportError(message, cause) {
8
- return createAcexError({
9
- code: "TRANSPORT_UNAVAILABLE",
10
- message,
11
- retryable: true,
12
- exchange: "binance",
13
- ...(cause === undefined ? {} : { cause }),
14
- });
15
- }
16
- function createPayloadError(message, cause) {
17
- return createAcexError({
18
- code: "VALIDATION_ERROR",
19
- message,
20
- retryable: false,
21
- exchange: "binance",
22
- ...(cause === undefined ? {} : { cause }),
23
- });
24
- }
25
- export function createBinanceMarketRestClient(input = {}) {
26
- const fetchImpl = input.fetchImpl ?? fetch;
27
- const baseUrls = input.baseUrls ?? DEFAULT_BASE_URLS;
28
- async function getJson(url) {
29
- let response;
30
- try {
31
- response = await fetchImpl(url);
32
- }
33
- catch (error) {
34
- throw createTransportError(`binance market metadata request failed: ${url}`, error);
35
- }
36
- if (!response.ok) {
37
- throw createTransportError(`binance market metadata request failed: ${url} (${response.status})`);
38
- }
39
- try {
40
- return (await response.json());
41
- }
42
- catch (error) {
43
- throw createPayloadError(`invalid binance market metadata payload: ${url}`, error);
44
- }
45
- }
46
- function requireSymbolsArray(family, payload) {
47
- if (!Array.isArray(payload.symbols)) {
48
- throw createPayloadError(`invalid binance market metadata payload: ${family}`);
49
- }
50
- return payload;
51
- }
52
- return {
53
- async fetchAllExchangeInfo() {
54
- const [spot, usdm, coinm] = await Promise.all([
55
- getJson(`${baseUrls.spot}/api/v3/exchangeInfo`),
56
- getJson(`${baseUrls.usdm}/fapi/v1/exchangeInfo`),
57
- getJson(`${baseUrls.coinm}/dapi/v1/exchangeInfo`),
58
- ]);
59
- return {
60
- spot: requireSymbolsArray("spot", spot),
61
- usdm: requireSymbolsArray("usdm", usdm),
62
- coinm: requireSymbolsArray("coinm", coinm),
63
- };
64
- },
65
- };
66
- }
@@ -1,9 +0,0 @@
1
- import type { BinanceCoinmExchangeInfo, BinanceMarketIndex, BinanceMarketRecord, BinanceSpotExchangeInfo, BinanceUsdmExchangeInfo } from "./market-types.js";
2
- export declare function buildBinanceMarketIndex(input: {
3
- spot: BinanceSpotExchangeInfo;
4
- usdm: BinanceUsdmExchangeInfo;
5
- coinm: BinanceCoinmExchangeInfo;
6
- }): BinanceMarketIndex;
7
- export declare function requireMarketRecord(index: BinanceMarketIndex, unifiedSymbol: string): BinanceMarketRecord;
8
- export declare function requireFundingMarketRecord(index: BinanceMarketIndex, unifiedSymbol: string): BinanceMarketRecord;
9
- export declare function getRecordByNativeSymbol(index: BinanceMarketIndex, family: BinanceMarketRecord["family"], nativeSymbol: string): BinanceMarketRecord | undefined;