@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.
- package/README.md +92 -285
- package/index.ts +1 -0
- package/package.json +40 -23
- package/src/adapters/binance/adapter.ts +80 -0
- package/src/adapters/binance/book-ticker.ts +123 -0
- package/src/adapters/binance/mark-price.ts +126 -0
- package/src/adapters/binance/market-catalog.ts +258 -0
- package/src/adapters/binance/private-adapter.ts +833 -0
- package/src/adapters/types.ts +219 -0
- package/src/client/context.ts +123 -0
- package/src/client/create-client.ts +6 -0
- package/src/client/private-subscription-coordinator.ts +512 -0
- package/src/client/runtime.ts +410 -0
- package/src/errors.ts +27 -0
- package/src/index.ts +5 -0
- package/src/internal/async-event-bus.ts +100 -0
- package/src/internal/filters.ts +117 -0
- package/src/internal/managed-websocket.ts +280 -0
- package/src/managers/account-manager.ts +609 -0
- package/src/managers/market-manager.ts +889 -0
- package/src/managers/order-manager.ts +685 -0
- package/src/types/account.ts +157 -0
- package/src/types/client.ts +79 -0
- package/src/types/index.ts +5 -0
- package/src/types/market.ts +150 -0
- package/src/types/order.ts +177 -0
- package/src/types/shared.ts +93 -0
- package/dist/adapters/binance/composite-adapter.d.ts +0 -116
- package/dist/adapters/binance/composite-adapter.js +0 -121
- package/dist/adapters/binance/market-types.d.ts +0 -63
- package/dist/adapters/binance/market-types.js +0 -1
- package/dist/adapters/binance/native-market-adapter.d.ts +0 -102
- package/dist/adapters/binance/native-market-adapter.js +0 -455
- package/dist/adapters/binance/normalizers.d.ts +0 -8
- package/dist/adapters/binance/normalizers.js +0 -123
- package/dist/adapters/binance/rest-client.d.ts +0 -17
- package/dist/adapters/binance/rest-client.js +0 -66
- package/dist/adapters/binance/symbol-router.d.ts +0 -9
- package/dist/adapters/binance/symbol-router.js +0 -174
- package/dist/adapters/binance/ws-client.d.ts +0 -24
- package/dist/adapters/binance/ws-client.js +0 -261
- package/dist/adapters/ccxt/aster-ccxt-adapter.d.ts +0 -157
- package/dist/adapters/ccxt/aster-ccxt-adapter.js +0 -272
- package/dist/adapters/ccxt/binance-usdm-ccxt-adapter.d.ts +0 -180
- package/dist/adapters/ccxt/binance-usdm-ccxt-adapter.js +0 -539
- package/dist/adapters/ccxt/binance-usdm-exchange.d.ts +0 -22
- package/dist/adapters/ccxt/binance-usdm-exchange.js +0 -23
- package/dist/adapters/fake/fake-aster-adapter.d.ts +0 -130
- package/dist/adapters/fake/fake-aster-adapter.js +0 -283
- package/dist/adapters/types.d.ts +0 -210
- package/dist/adapters/types.js +0 -1
- package/dist/core/client.d.ts +0 -50
- package/dist/core/client.js +0 -403
- package/dist/core/recovery.d.ts +0 -22
- package/dist/core/recovery.js +0 -18
- package/dist/core/runtime.d.ts +0 -26
- package/dist/core/runtime.js +0 -150
- package/dist/errors/acex-error.d.ts +0 -25
- package/dist/errors/acex-error.js +0 -54
- package/dist/index.d.ts +0 -6
- package/dist/index.js +0 -3
- package/dist/managers/account-manager.d.ts +0 -41
- package/dist/managers/account-manager.js +0 -80
- package/dist/managers/market-manager.d.ts +0 -16
- package/dist/managers/market-manager.js +0 -28
- package/dist/managers/order-manager.d.ts +0 -87
- package/dist/managers/order-manager.js +0 -122
- package/dist/runtime/async-queue.d.ts +0 -8
- package/dist/runtime/async-queue.js +0 -88
- package/dist/runtime/request-id.d.ts +0 -1
- package/dist/runtime/request-id.js +0 -5
- package/dist/runtime/ws-connection-supervisor.d.ts +0 -76
- package/dist/runtime/ws-connection-supervisor.js +0 -522
- package/dist/store/account-store.d.ts +0 -52
- package/dist/store/account-store.js +0 -18
- package/dist/store/health-store.d.ts +0 -16
- package/dist/store/health-store.js +0 -29
- package/dist/store/market-store.d.ts +0 -42
- package/dist/store/market-store.js +0 -51
- package/dist/store/order-store.d.ts +0 -38
- package/dist/store/order-store.js +0 -49
- package/dist/testing/create-fake-runtime.d.ts +0 -5
- package/dist/testing/create-fake-runtime.js +0 -7
- package/dist/types/public.d.ts +0 -5
- package/dist/types/public.js +0 -1
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
type TimerHandle = ReturnType<typeof setTimeout>;
|
|
2
|
+
|
|
3
|
+
export type WebSocketFactory = (url: string) => WebSocket;
|
|
4
|
+
|
|
5
|
+
export interface ManagedWebSocketWatchdogOptions {
|
|
6
|
+
staleAfterMs: number;
|
|
7
|
+
onStale(staleAt: number): void;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface ManagedWebSocketReconnectOptions {
|
|
11
|
+
initialDelayMs: number;
|
|
12
|
+
maxDelayMs: number;
|
|
13
|
+
backoffMultiplier?: number;
|
|
14
|
+
reconnectWithoutMessages?: boolean;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface ManagedWebSocketOptions<TMessage> {
|
|
18
|
+
url: string;
|
|
19
|
+
initialMessageTimeoutMs: number;
|
|
20
|
+
readyWhen?: "message" | "open";
|
|
21
|
+
parseMessage(data: string): TMessage | undefined;
|
|
22
|
+
onMessage(message: TMessage, receivedAt: number): void;
|
|
23
|
+
onUnexpectedClose(event: CloseEvent): void;
|
|
24
|
+
onOpen?(): void;
|
|
25
|
+
onError?(event: Event): void;
|
|
26
|
+
messageWatchdog?: ManagedWebSocketWatchdogOptions;
|
|
27
|
+
reconnect?: ManagedWebSocketReconnectOptions;
|
|
28
|
+
now?: () => number;
|
|
29
|
+
createWebSocket?: WebSocketFactory;
|
|
30
|
+
setTimer?: typeof setTimeout;
|
|
31
|
+
clearTimer?: typeof clearTimeout;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface ManagedWebSocketSession {
|
|
35
|
+
readonly ready: Promise<void>;
|
|
36
|
+
close(): void;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function toError(value: unknown, fallback: string): Error {
|
|
40
|
+
if (value instanceof Error) {
|
|
41
|
+
return value;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return new Error(fallback);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function createManagedWebSocket<TMessage>(
|
|
48
|
+
options: ManagedWebSocketOptions<TMessage>,
|
|
49
|
+
): ManagedWebSocketSession {
|
|
50
|
+
const now = options.now ?? Date.now;
|
|
51
|
+
const setTimer = options.setTimer ?? setTimeout;
|
|
52
|
+
const clearTimer = options.clearTimer ?? clearTimeout;
|
|
53
|
+
const createWebSocket =
|
|
54
|
+
options.createWebSocket ?? ((url: string) => new WebSocket(url));
|
|
55
|
+
const messageWatchdog = options.messageWatchdog;
|
|
56
|
+
const reconnect = options.reconnect;
|
|
57
|
+
const reconnectMultiplier = reconnect?.backoffMultiplier ?? 2;
|
|
58
|
+
const readyWhen = options.readyWhen ?? "message";
|
|
59
|
+
|
|
60
|
+
let closed = false;
|
|
61
|
+
let staleNotified = false;
|
|
62
|
+
let hasMessage = false;
|
|
63
|
+
let lastMessageAt = now();
|
|
64
|
+
let initialTimeout: TimerHandle | undefined;
|
|
65
|
+
let staleTimeout: TimerHandle | undefined;
|
|
66
|
+
let reconnectTimeout: TimerHandle | undefined;
|
|
67
|
+
let reconnectAttempts = 0;
|
|
68
|
+
let resolveReady: (() => void) | undefined;
|
|
69
|
+
let rejectReady: ((error: Error) => void) | undefined;
|
|
70
|
+
let activeSocket: WebSocket | undefined;
|
|
71
|
+
|
|
72
|
+
const ready = new Promise<void>((resolve, reject) => {
|
|
73
|
+
resolveReady = resolve;
|
|
74
|
+
rejectReady = reject;
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
const clearTimers = () => {
|
|
78
|
+
if (initialTimeout) {
|
|
79
|
+
clearTimer(initialTimeout);
|
|
80
|
+
initialTimeout = undefined;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (staleTimeout) {
|
|
84
|
+
clearTimer(staleTimeout);
|
|
85
|
+
staleTimeout = undefined;
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
const clearReconnectTimer = () => {
|
|
90
|
+
if (reconnectTimeout) {
|
|
91
|
+
clearTimer(reconnectTimeout);
|
|
92
|
+
reconnectTimeout = undefined;
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
const rejectIfPending = (error: Error) => {
|
|
97
|
+
if (!resolveReady || !rejectReady) {
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const reject = rejectReady;
|
|
102
|
+
resolveReady = undefined;
|
|
103
|
+
rejectReady = undefined;
|
|
104
|
+
reject(error);
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
const resolveIfPending = () => {
|
|
108
|
+
if (!resolveReady || !rejectReady) {
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const resolve = resolveReady;
|
|
113
|
+
resolveReady = undefined;
|
|
114
|
+
rejectReady = undefined;
|
|
115
|
+
resolve();
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
const scheduleStaleTimeout = () => {
|
|
119
|
+
if (closed || !messageWatchdog) {
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (staleTimeout) {
|
|
124
|
+
clearTimer(staleTimeout);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
staleTimeout = setTimer(() => {
|
|
128
|
+
if (closed || staleNotified) {
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
staleNotified = true;
|
|
133
|
+
messageWatchdog.onStale(now());
|
|
134
|
+
}, messageWatchdog.staleAfterMs);
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
const scheduleReconnect = () => {
|
|
138
|
+
if (
|
|
139
|
+
closed ||
|
|
140
|
+
!reconnect ||
|
|
141
|
+
reconnectTimeout ||
|
|
142
|
+
(!hasMessage && !reconnect.reconnectWithoutMessages)
|
|
143
|
+
) {
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const delay = Math.min(
|
|
148
|
+
reconnect.initialDelayMs * reconnectMultiplier ** reconnectAttempts,
|
|
149
|
+
reconnect.maxDelayMs,
|
|
150
|
+
);
|
|
151
|
+
reconnectAttempts += 1;
|
|
152
|
+
reconnectTimeout = setTimer(() => {
|
|
153
|
+
reconnectTimeout = undefined;
|
|
154
|
+
connect();
|
|
155
|
+
}, delay);
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
const connect = () => {
|
|
159
|
+
if (closed) {
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const socket = createWebSocket(options.url);
|
|
164
|
+
activeSocket = socket;
|
|
165
|
+
|
|
166
|
+
if (!hasMessage) {
|
|
167
|
+
initialTimeout = setTimer(() => {
|
|
168
|
+
if (closed || hasMessage || activeSocket !== socket) {
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
rejectIfPending(
|
|
173
|
+
new Error("Timed out waiting for the first websocket message"),
|
|
174
|
+
);
|
|
175
|
+
socket.close(1000, "initial message timeout");
|
|
176
|
+
}, options.initialMessageTimeoutMs);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
if (messageWatchdog) {
|
|
180
|
+
scheduleStaleTimeout();
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
socket.addEventListener("open", () => {
|
|
184
|
+
if (closed || activeSocket !== socket) {
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
options.onOpen?.();
|
|
189
|
+
if (readyWhen === "open") {
|
|
190
|
+
resolveIfPending();
|
|
191
|
+
}
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
socket.addEventListener("message", (event) => {
|
|
195
|
+
if (closed || activeSocket !== socket || typeof event.data !== "string") {
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
let parsed: TMessage | undefined;
|
|
200
|
+
try {
|
|
201
|
+
parsed = options.parseMessage(event.data);
|
|
202
|
+
} catch (error) {
|
|
203
|
+
options.onError?.(
|
|
204
|
+
new ErrorEvent("error", {
|
|
205
|
+
error: toError(error, "Failed to parse websocket message"),
|
|
206
|
+
}),
|
|
207
|
+
);
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
if (!parsed) {
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
hasMessage = true;
|
|
216
|
+
staleNotified = false;
|
|
217
|
+
lastMessageAt = now();
|
|
218
|
+
reconnectAttempts = 0;
|
|
219
|
+
|
|
220
|
+
if (initialTimeout) {
|
|
221
|
+
clearTimer(initialTimeout);
|
|
222
|
+
initialTimeout = undefined;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
if (messageWatchdog) {
|
|
226
|
+
scheduleStaleTimeout();
|
|
227
|
+
}
|
|
228
|
+
options.onMessage(parsed, lastMessageAt);
|
|
229
|
+
if (readyWhen === "message") {
|
|
230
|
+
resolveIfPending();
|
|
231
|
+
}
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
socket.addEventListener("error", (event) => {
|
|
235
|
+
if (closed || activeSocket !== socket) {
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
options.onError?.(event);
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
socket.addEventListener("close", (event) => {
|
|
243
|
+
if (closed || activeSocket !== socket) {
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
activeSocket = undefined;
|
|
248
|
+
clearTimers();
|
|
249
|
+
|
|
250
|
+
if (!hasMessage) {
|
|
251
|
+
rejectIfPending(
|
|
252
|
+
toError(
|
|
253
|
+
event.reason || undefined,
|
|
254
|
+
"WebSocket closed before the first market update arrived",
|
|
255
|
+
),
|
|
256
|
+
);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
options.onUnexpectedClose(event);
|
|
260
|
+
scheduleReconnect();
|
|
261
|
+
});
|
|
262
|
+
};
|
|
263
|
+
|
|
264
|
+
connect();
|
|
265
|
+
|
|
266
|
+
return {
|
|
267
|
+
ready,
|
|
268
|
+
close() {
|
|
269
|
+
if (closed) {
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
closed = true;
|
|
274
|
+
clearTimers();
|
|
275
|
+
clearReconnectTimer();
|
|
276
|
+
activeSocket?.close(1000, "manual close");
|
|
277
|
+
activeSocket = undefined;
|
|
278
|
+
},
|
|
279
|
+
};
|
|
280
|
+
}
|