@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,512 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
PrivateUserDataAdapter,
|
|
3
|
+
StreamHandle,
|
|
4
|
+
} from "../adapters/types.ts";
|
|
5
|
+
import { AcexError } from "../errors.ts";
|
|
6
|
+
import type { AccountRuntimeOptions, Exchange } from "../types/index.ts";
|
|
7
|
+
import type {
|
|
8
|
+
ClientContext,
|
|
9
|
+
PrivateAccountDataConsumer,
|
|
10
|
+
PrivateOrderDataConsumer,
|
|
11
|
+
RegisteredAccountRecord,
|
|
12
|
+
} from "./context.ts";
|
|
13
|
+
|
|
14
|
+
interface PrivateSubscriptionRecord {
|
|
15
|
+
accountId: string;
|
|
16
|
+
exchange: Exchange;
|
|
17
|
+
accountSubscribed: boolean;
|
|
18
|
+
ordersSubscribed: boolean;
|
|
19
|
+
accountReady: boolean;
|
|
20
|
+
orderReady: boolean;
|
|
21
|
+
stream?: StreamHandle;
|
|
22
|
+
startPromise?: Promise<void>;
|
|
23
|
+
reconcilePromise?: Promise<void>;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const DEFAULT_STREAM_OPEN_TIMEOUT_MS = 15_000;
|
|
27
|
+
const DEFAULT_STREAM_RECONNECT_DELAY_MS = 1_000;
|
|
28
|
+
const DEFAULT_STREAM_RECONNECT_MAX_DELAY_MS = 10_000;
|
|
29
|
+
const DEFAULT_LISTEN_KEY_KEEPALIVE_MS = 30 * 60 * 1_000;
|
|
30
|
+
|
|
31
|
+
export class PrivateSubscriptionCoordinator {
|
|
32
|
+
private readonly context: ClientContext;
|
|
33
|
+
private readonly adapter: PrivateUserDataAdapter;
|
|
34
|
+
private readonly accountConsumer: PrivateAccountDataConsumer;
|
|
35
|
+
private readonly orderConsumer: PrivateOrderDataConsumer;
|
|
36
|
+
private readonly streamOpenTimeoutMs: number;
|
|
37
|
+
private readonly streamReconnectDelayMs: number;
|
|
38
|
+
private readonly streamReconnectMaxDelayMs: number;
|
|
39
|
+
private readonly listenKeyKeepAliveMs: number;
|
|
40
|
+
private readonly records = new Map<string, PrivateSubscriptionRecord>();
|
|
41
|
+
|
|
42
|
+
constructor(
|
|
43
|
+
context: ClientContext,
|
|
44
|
+
adapter: PrivateUserDataAdapter,
|
|
45
|
+
accountConsumer: PrivateAccountDataConsumer,
|
|
46
|
+
orderConsumer: PrivateOrderDataConsumer,
|
|
47
|
+
options: AccountRuntimeOptions = {},
|
|
48
|
+
) {
|
|
49
|
+
this.context = context;
|
|
50
|
+
this.adapter = adapter;
|
|
51
|
+
this.accountConsumer = accountConsumer;
|
|
52
|
+
this.orderConsumer = orderConsumer;
|
|
53
|
+
this.streamOpenTimeoutMs =
|
|
54
|
+
options.streamOpenTimeoutMs ?? DEFAULT_STREAM_OPEN_TIMEOUT_MS;
|
|
55
|
+
this.streamReconnectDelayMs =
|
|
56
|
+
options.streamReconnectDelayMs ?? DEFAULT_STREAM_RECONNECT_DELAY_MS;
|
|
57
|
+
this.streamReconnectMaxDelayMs =
|
|
58
|
+
options.streamReconnectMaxDelayMs ??
|
|
59
|
+
DEFAULT_STREAM_RECONNECT_MAX_DELAY_MS;
|
|
60
|
+
this.listenKeyKeepAliveMs =
|
|
61
|
+
options.listenKeyKeepAliveMs ?? DEFAULT_LISTEN_KEY_KEEPALIVE_MS;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
async subscribeAccountFeed(accountId: string): Promise<void> {
|
|
65
|
+
const account = this.getAccount(accountId);
|
|
66
|
+
const record = this.getOrCreateRecord(account);
|
|
67
|
+
const needsPending = !record.stream && !record.startPromise;
|
|
68
|
+
record.accountSubscribed = true;
|
|
69
|
+
if (needsPending) {
|
|
70
|
+
this.accountConsumer.onPrivateAccountPending(accountId, record.exchange);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
try {
|
|
74
|
+
await this.ensureStream(record, account);
|
|
75
|
+
await this.bootstrapAccount(record, account);
|
|
76
|
+
} catch (error) {
|
|
77
|
+
record.accountSubscribed = false;
|
|
78
|
+
this.closeIfUnused(record);
|
|
79
|
+
throw error;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
unsubscribeAccountFeed(accountId: string): void {
|
|
84
|
+
const record = this.records.get(accountId);
|
|
85
|
+
if (!record) {
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
record.accountSubscribed = false;
|
|
90
|
+
this.closeIfUnused(record);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
async subscribeOrderFeed(accountId: string): Promise<void> {
|
|
94
|
+
const account = this.getAccount(accountId);
|
|
95
|
+
const record = this.getOrCreateRecord(account);
|
|
96
|
+
const needsPending = !record.stream && !record.startPromise;
|
|
97
|
+
record.ordersSubscribed = true;
|
|
98
|
+
if (needsPending) {
|
|
99
|
+
this.orderConsumer.onPrivateOrderPending(accountId, record.exchange);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
try {
|
|
103
|
+
await this.ensureStream(record, account);
|
|
104
|
+
await this.bootstrapOrders(record, account);
|
|
105
|
+
} catch (error) {
|
|
106
|
+
record.ordersSubscribed = false;
|
|
107
|
+
this.closeIfUnused(record);
|
|
108
|
+
throw error;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
unsubscribeOrderFeed(accountId: string): void {
|
|
113
|
+
const record = this.records.get(accountId);
|
|
114
|
+
if (!record) {
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
record.ordersSubscribed = false;
|
|
119
|
+
this.closeIfUnused(record);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
onClientStarted(): void {
|
|
123
|
+
for (const record of this.records.values()) {
|
|
124
|
+
if (!this.isActive(record)) {
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (record.accountSubscribed) {
|
|
129
|
+
this.accountConsumer.onPrivateAccountPending(
|
|
130
|
+
record.accountId,
|
|
131
|
+
record.exchange,
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
if (record.ordersSubscribed) {
|
|
135
|
+
this.orderConsumer.onPrivateOrderPending(
|
|
136
|
+
record.accountId,
|
|
137
|
+
record.exchange,
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
void this.resumeRecord(record);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
onClientStopping(): void {
|
|
146
|
+
for (const record of this.records.values()) {
|
|
147
|
+
this.closeStream(record);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
onAccountRemoved(accountId: string): void {
|
|
152
|
+
const record = this.records.get(accountId);
|
|
153
|
+
if (!record) {
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
this.closeStream(record);
|
|
158
|
+
this.records.delete(accountId);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
onCredentialsUpdated(accountId: string): void {
|
|
162
|
+
const record = this.records.get(accountId);
|
|
163
|
+
if (!record || !this.isActive(record)) {
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
if (record.accountSubscribed) {
|
|
168
|
+
this.accountConsumer.onPrivateAccountPending(accountId, record.exchange);
|
|
169
|
+
}
|
|
170
|
+
if (record.ordersSubscribed) {
|
|
171
|
+
this.orderConsumer.onPrivateOrderPending(accountId, record.exchange);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
void this.resumeRecord(record);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
private async resumeRecord(record: PrivateSubscriptionRecord): Promise<void> {
|
|
178
|
+
const account = this.getAccount(record.accountId);
|
|
179
|
+
this.closeStream(record);
|
|
180
|
+
|
|
181
|
+
try {
|
|
182
|
+
await this.ensureStream(record, account);
|
|
183
|
+
if (record.accountSubscribed) {
|
|
184
|
+
await this.bootstrapAccount(record, account);
|
|
185
|
+
}
|
|
186
|
+
if (record.ordersSubscribed) {
|
|
187
|
+
await this.bootstrapOrders(record, account);
|
|
188
|
+
}
|
|
189
|
+
} catch {
|
|
190
|
+
// Errors are already published to the runtime error bus.
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
private getAccount(accountId: string): RegisteredAccountRecord {
|
|
195
|
+
const account = this.context.getRegisteredAccount(accountId);
|
|
196
|
+
if (account.exchange !== this.adapter.exchange) {
|
|
197
|
+
throw new AcexError(
|
|
198
|
+
"EXCHANGE_NOT_SUPPORTED",
|
|
199
|
+
`Exchange is not supported yet: ${account.exchange}`,
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
return account;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
private getOrCreateRecord(
|
|
207
|
+
account: RegisteredAccountRecord,
|
|
208
|
+
): PrivateSubscriptionRecord {
|
|
209
|
+
const existing = this.records.get(account.accountId);
|
|
210
|
+
if (existing) {
|
|
211
|
+
return existing;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
const record: PrivateSubscriptionRecord = {
|
|
215
|
+
accountId: account.accountId,
|
|
216
|
+
exchange: account.exchange,
|
|
217
|
+
accountSubscribed: false,
|
|
218
|
+
ordersSubscribed: false,
|
|
219
|
+
accountReady: false,
|
|
220
|
+
orderReady: false,
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
this.records.set(account.accountId, record);
|
|
224
|
+
return record;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
private isActive(record: PrivateSubscriptionRecord): boolean {
|
|
228
|
+
return record.accountSubscribed || record.ordersSubscribed;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
private closeIfUnused(record: PrivateSubscriptionRecord): void {
|
|
232
|
+
if (this.isActive(record)) {
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
this.closeStream(record);
|
|
237
|
+
this.records.delete(record.accountId);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
private closeStream(record: PrivateSubscriptionRecord): void {
|
|
241
|
+
record.stream?.close();
|
|
242
|
+
record.stream = undefined;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
private async ensureStream(
|
|
246
|
+
record: PrivateSubscriptionRecord,
|
|
247
|
+
account: RegisteredAccountRecord,
|
|
248
|
+
): Promise<void> {
|
|
249
|
+
if (record.stream) {
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
if (!record.startPromise) {
|
|
254
|
+
record.startPromise = this.startStream(record, account);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
try {
|
|
258
|
+
await record.startPromise;
|
|
259
|
+
} finally {
|
|
260
|
+
if (record.startPromise) {
|
|
261
|
+
record.startPromise = undefined;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
private async startStream(
|
|
267
|
+
record: PrivateSubscriptionRecord,
|
|
268
|
+
account: RegisteredAccountRecord,
|
|
269
|
+
): Promise<void> {
|
|
270
|
+
const credentials = account.credentials;
|
|
271
|
+
if (!credentials) {
|
|
272
|
+
throw new AcexError(
|
|
273
|
+
"CREDENTIALS_MISSING",
|
|
274
|
+
`Account credentials are required for private subscriptions: ${account.accountId}`,
|
|
275
|
+
);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
const stream = this.adapter.createPrivateStream(
|
|
279
|
+
credentials,
|
|
280
|
+
{
|
|
281
|
+
onAccountUpdate: (update) => {
|
|
282
|
+
if (!record.accountSubscribed) {
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
record.accountReady = true;
|
|
287
|
+
this.accountConsumer.onPrivateAccountUpdate(
|
|
288
|
+
record.accountId,
|
|
289
|
+
record.exchange,
|
|
290
|
+
update,
|
|
291
|
+
);
|
|
292
|
+
},
|
|
293
|
+
onOrderUpdate: (update) => {
|
|
294
|
+
if (!record.ordersSubscribed) {
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
record.orderReady = true;
|
|
299
|
+
this.orderConsumer.onPrivateOrderUpdate(
|
|
300
|
+
record.accountId,
|
|
301
|
+
record.exchange,
|
|
302
|
+
update,
|
|
303
|
+
);
|
|
304
|
+
},
|
|
305
|
+
onDisconnected: () => {
|
|
306
|
+
if (record.accountSubscribed) {
|
|
307
|
+
this.accountConsumer.onPrivateAccountStreamState(
|
|
308
|
+
record.accountId,
|
|
309
|
+
record.exchange,
|
|
310
|
+
{
|
|
311
|
+
runtimeStatus: "reconnecting",
|
|
312
|
+
ready: record.accountReady,
|
|
313
|
+
reason: "ws_disconnected",
|
|
314
|
+
},
|
|
315
|
+
);
|
|
316
|
+
}
|
|
317
|
+
if (record.ordersSubscribed) {
|
|
318
|
+
this.orderConsumer.onPrivateOrderStreamState(
|
|
319
|
+
record.accountId,
|
|
320
|
+
record.exchange,
|
|
321
|
+
{
|
|
322
|
+
runtimeStatus: "reconnecting",
|
|
323
|
+
ready: record.orderReady,
|
|
324
|
+
reason: "ws_disconnected",
|
|
325
|
+
},
|
|
326
|
+
);
|
|
327
|
+
}
|
|
328
|
+
},
|
|
329
|
+
onReconnected: () => {
|
|
330
|
+
if (!record.reconcilePromise) {
|
|
331
|
+
record.reconcilePromise = this.reconcileRecord(record)
|
|
332
|
+
.catch(() => {})
|
|
333
|
+
.finally(() => {
|
|
334
|
+
record.reconcilePromise = undefined;
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
},
|
|
338
|
+
onError: (error) => {
|
|
339
|
+
this.context.publishRuntimeError("adapter", error, {
|
|
340
|
+
accountId: record.accountId,
|
|
341
|
+
exchange: record.exchange,
|
|
342
|
+
});
|
|
343
|
+
},
|
|
344
|
+
},
|
|
345
|
+
{
|
|
346
|
+
openTimeoutMs: this.streamOpenTimeoutMs,
|
|
347
|
+
reconnectDelayMs: this.streamReconnectDelayMs,
|
|
348
|
+
reconnectMaxDelayMs: this.streamReconnectMaxDelayMs,
|
|
349
|
+
listenKeyKeepAliveMs: this.listenKeyKeepAliveMs,
|
|
350
|
+
now: () => this.context.now(),
|
|
351
|
+
},
|
|
352
|
+
account.options,
|
|
353
|
+
);
|
|
354
|
+
|
|
355
|
+
record.stream = stream;
|
|
356
|
+
|
|
357
|
+
try {
|
|
358
|
+
await stream.ready;
|
|
359
|
+
} catch (error) {
|
|
360
|
+
this.closeStream(record);
|
|
361
|
+
const runtimeError =
|
|
362
|
+
error instanceof Error
|
|
363
|
+
? error
|
|
364
|
+
: new Error("Failed to open Binance private stream");
|
|
365
|
+
this.context.publishRuntimeError("adapter", runtimeError, {
|
|
366
|
+
accountId: record.accountId,
|
|
367
|
+
exchange: record.exchange,
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
if (record.accountSubscribed) {
|
|
371
|
+
this.accountConsumer.onPrivateAccountStreamState(
|
|
372
|
+
record.accountId,
|
|
373
|
+
record.exchange,
|
|
374
|
+
{
|
|
375
|
+
runtimeStatus: "degraded",
|
|
376
|
+
ready: record.accountReady,
|
|
377
|
+
reason: "ws_disconnected",
|
|
378
|
+
},
|
|
379
|
+
);
|
|
380
|
+
}
|
|
381
|
+
if (record.ordersSubscribed) {
|
|
382
|
+
this.orderConsumer.onPrivateOrderStreamState(
|
|
383
|
+
record.accountId,
|
|
384
|
+
record.exchange,
|
|
385
|
+
{
|
|
386
|
+
runtimeStatus: "degraded",
|
|
387
|
+
ready: record.orderReady,
|
|
388
|
+
reason: "ws_disconnected",
|
|
389
|
+
},
|
|
390
|
+
);
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
throw runtimeError;
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
private async reconcileRecord(
|
|
398
|
+
record: PrivateSubscriptionRecord,
|
|
399
|
+
): Promise<void> {
|
|
400
|
+
const account = this.getAccount(record.accountId);
|
|
401
|
+
|
|
402
|
+
if (record.accountSubscribed) {
|
|
403
|
+
this.accountConsumer.onPrivateAccountPending(
|
|
404
|
+
record.accountId,
|
|
405
|
+
record.exchange,
|
|
406
|
+
);
|
|
407
|
+
await this.bootstrapAccount(record, account);
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
if (record.ordersSubscribed) {
|
|
411
|
+
this.orderConsumer.onPrivateOrderPending(
|
|
412
|
+
record.accountId,
|
|
413
|
+
record.exchange,
|
|
414
|
+
);
|
|
415
|
+
await this.bootstrapOrders(record, account);
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
private async bootstrapAccount(
|
|
420
|
+
record: PrivateSubscriptionRecord,
|
|
421
|
+
account: RegisteredAccountRecord,
|
|
422
|
+
): Promise<void> {
|
|
423
|
+
try {
|
|
424
|
+
const bootstrap = await this.adapter.bootstrapAccount(
|
|
425
|
+
account.credentials ?? {},
|
|
426
|
+
account.options,
|
|
427
|
+
);
|
|
428
|
+
if (!record.accountSubscribed) {
|
|
429
|
+
return;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
record.accountReady = true;
|
|
433
|
+
this.accountConsumer.onPrivateAccountBootstrap(
|
|
434
|
+
record.accountId,
|
|
435
|
+
record.exchange,
|
|
436
|
+
bootstrap,
|
|
437
|
+
);
|
|
438
|
+
} catch (error) {
|
|
439
|
+
record.accountReady = false;
|
|
440
|
+
this.context.publishRuntimeError(
|
|
441
|
+
"adapter",
|
|
442
|
+
error instanceof Error
|
|
443
|
+
? error
|
|
444
|
+
: new Error("Failed to bootstrap Binance private account state"),
|
|
445
|
+
{
|
|
446
|
+
accountId: record.accountId,
|
|
447
|
+
exchange: record.exchange,
|
|
448
|
+
},
|
|
449
|
+
);
|
|
450
|
+
this.accountConsumer.onPrivateAccountStreamState(
|
|
451
|
+
record.accountId,
|
|
452
|
+
record.exchange,
|
|
453
|
+
{
|
|
454
|
+
runtimeStatus: "degraded",
|
|
455
|
+
ready: false,
|
|
456
|
+
reason: "auth_failed",
|
|
457
|
+
},
|
|
458
|
+
);
|
|
459
|
+
throw new AcexError(
|
|
460
|
+
"ACCOUNT_BOOTSTRAP_FAILED",
|
|
461
|
+
`Failed to bootstrap account data: ${record.accountId}`,
|
|
462
|
+
);
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
private async bootstrapOrders(
|
|
467
|
+
record: PrivateSubscriptionRecord,
|
|
468
|
+
account: RegisteredAccountRecord,
|
|
469
|
+
): Promise<void> {
|
|
470
|
+
try {
|
|
471
|
+
const snapshots = await this.adapter.bootstrapOpenOrders(
|
|
472
|
+
account.credentials ?? {},
|
|
473
|
+
account.options,
|
|
474
|
+
);
|
|
475
|
+
if (!record.ordersSubscribed) {
|
|
476
|
+
return;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
record.orderReady = true;
|
|
480
|
+
this.orderConsumer.onPrivateOrderBootstrap(
|
|
481
|
+
record.accountId,
|
|
482
|
+
record.exchange,
|
|
483
|
+
snapshots,
|
|
484
|
+
);
|
|
485
|
+
} catch (error) {
|
|
486
|
+
record.orderReady = false;
|
|
487
|
+
this.context.publishRuntimeError(
|
|
488
|
+
"adapter",
|
|
489
|
+
error instanceof Error
|
|
490
|
+
? error
|
|
491
|
+
: new Error("Failed to bootstrap Binance private order state"),
|
|
492
|
+
{
|
|
493
|
+
accountId: record.accountId,
|
|
494
|
+
exchange: record.exchange,
|
|
495
|
+
},
|
|
496
|
+
);
|
|
497
|
+
this.orderConsumer.onPrivateOrderStreamState(
|
|
498
|
+
record.accountId,
|
|
499
|
+
record.exchange,
|
|
500
|
+
{
|
|
501
|
+
runtimeStatus: "degraded",
|
|
502
|
+
ready: false,
|
|
503
|
+
reason: "auth_failed",
|
|
504
|
+
},
|
|
505
|
+
);
|
|
506
|
+
throw new AcexError(
|
|
507
|
+
"ORDER_BOOTSTRAP_FAILED",
|
|
508
|
+
`Failed to bootstrap order data: ${record.accountId}`,
|
|
509
|
+
);
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
}
|