@imbingox/acex 0.1.0 → 0.3.0-beta.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 +96 -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 +912 -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 +152 -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
package/dist/core/client.js
DELETED
|
@@ -1,403 +0,0 @@
|
|
|
1
|
-
import { BinanceCompositeAdapter } from "../adapters/binance/composite-adapter.js";
|
|
2
|
-
import { BinanceNativeMarketAdapter } from "../adapters/binance/native-market-adapter.js";
|
|
3
|
-
import { CcxtBinanceUsdMAdapter } from "../adapters/ccxt/binance-usdm-ccxt-adapter.js";
|
|
4
|
-
import { createBinanceUsdmExchange, } from "../adapters/ccxt/binance-usdm-exchange.js";
|
|
5
|
-
import { createAcexError, isAcexError } from "../errors/acex-error.js";
|
|
6
|
-
import { createIdleAccountManager } from "../managers/account-manager.js";
|
|
7
|
-
import { createIdleMarketManager } from "../managers/market-manager.js";
|
|
8
|
-
import { createIdleOrderManager } from "../managers/order-manager.js";
|
|
9
|
-
import { createAsyncQueue } from "../runtime/async-queue.js";
|
|
10
|
-
import { createHealthStore } from "../store/health-store.js";
|
|
11
|
-
import { IMPLEMENTED_EXCHANGES } from "../types/public.js";
|
|
12
|
-
import { createRuntime } from "./runtime.js";
|
|
13
|
-
const PUBLIC_EXCHANGE = IMPLEMENTED_EXCHANGES[0];
|
|
14
|
-
async function* emptyAsyncIterable() { }
|
|
15
|
-
export function createInternalClient(options = {}) {
|
|
16
|
-
let status = "idle";
|
|
17
|
-
const healthStore = createHealthStore();
|
|
18
|
-
const accounts = new Map();
|
|
19
|
-
const adapter = options.adapter;
|
|
20
|
-
const runtime = adapter === undefined ? undefined : createRuntime(adapter);
|
|
21
|
-
return {
|
|
22
|
-
market: runtime?.market ?? createIdleMarketManager(),
|
|
23
|
-
account: runtime?.account ?? createIdleAccountManager(),
|
|
24
|
-
order: runtime?.order ?? createIdleOrderManager(),
|
|
25
|
-
getStatus: () => status,
|
|
26
|
-
getHealth: () => healthStore.snapshot(),
|
|
27
|
-
watchErrors: () => runtime?.watchErrors() ?? emptyAsyncIterable(),
|
|
28
|
-
watchHealth: () => runtime?.watchHealth() ?? emptyAsyncIterable(),
|
|
29
|
-
async registerAccount(input) {
|
|
30
|
-
accounts.set(input.accountId, input);
|
|
31
|
-
return {
|
|
32
|
-
accountId: input.accountId,
|
|
33
|
-
exchange: input.exchange,
|
|
34
|
-
};
|
|
35
|
-
},
|
|
36
|
-
async start() {
|
|
37
|
-
status = "running";
|
|
38
|
-
healthStore.setClientStatus("running");
|
|
39
|
-
if (adapter !== undefined) {
|
|
40
|
-
await adapter.start();
|
|
41
|
-
healthStore.setExchangeHealth(adapter.exchange, adapter.getHealth());
|
|
42
|
-
}
|
|
43
|
-
},
|
|
44
|
-
async stop() {
|
|
45
|
-
status = "stopped";
|
|
46
|
-
healthStore.setClientStatus("stopped");
|
|
47
|
-
await adapter?.stop();
|
|
48
|
-
},
|
|
49
|
-
};
|
|
50
|
-
}
|
|
51
|
-
export function createPublicClient(options = {}, dependencies = {}) {
|
|
52
|
-
const idleMarket = createIdleMarketManager();
|
|
53
|
-
const idleAccount = createIdleAccountManager();
|
|
54
|
-
const idleOrder = createIdleOrderManager();
|
|
55
|
-
const accountEvents = createAsyncQueue({ maxBufferSize: 100 });
|
|
56
|
-
const errors = createAsyncQueue({ maxBufferSize: 100 });
|
|
57
|
-
const healthStore = createHealthStore();
|
|
58
|
-
const healthEvents = createAsyncQueue({ maxBufferSize: 100 });
|
|
59
|
-
const accounts = new Map();
|
|
60
|
-
let status = "idle";
|
|
61
|
-
let activeClient;
|
|
62
|
-
let activeAdapter;
|
|
63
|
-
const market = {
|
|
64
|
-
async subscribeL1Book(input) {
|
|
65
|
-
assertSupportedExchange(input.exchange);
|
|
66
|
-
const client = await requireStartedClient();
|
|
67
|
-
await client.market.subscribeL1Book(input);
|
|
68
|
-
syncHealth();
|
|
69
|
-
},
|
|
70
|
-
async subscribeFundingRate(input) {
|
|
71
|
-
assertSupportedExchange(input.exchange);
|
|
72
|
-
const client = await requireStartedClient();
|
|
73
|
-
await client.market.subscribeFundingRate(input);
|
|
74
|
-
syncHealth();
|
|
75
|
-
},
|
|
76
|
-
getL1Book(key) {
|
|
77
|
-
return getCurrentClient().market.getL1Book(key);
|
|
78
|
-
},
|
|
79
|
-
getFundingRate(key) {
|
|
80
|
-
return getCurrentClient().market.getFundingRate(key);
|
|
81
|
-
},
|
|
82
|
-
getMarketSnapshot(key) {
|
|
83
|
-
return getCurrentClient().market.getMarketSnapshot(key);
|
|
84
|
-
},
|
|
85
|
-
getMarketStatus(key) {
|
|
86
|
-
return getCurrentClient().market.getMarketStatus(key);
|
|
87
|
-
},
|
|
88
|
-
};
|
|
89
|
-
const account = {
|
|
90
|
-
async subscribeAccount(input) {
|
|
91
|
-
getRegisteredAccount(input.accountId);
|
|
92
|
-
const client = await requireStartedClient();
|
|
93
|
-
await client.account.subscribeAccount(input);
|
|
94
|
-
syncHealth();
|
|
95
|
-
},
|
|
96
|
-
getBalance(accountId, asset) {
|
|
97
|
-
return getCurrentClient().account.getBalance(accountId, asset);
|
|
98
|
-
},
|
|
99
|
-
getBalances(accountId) {
|
|
100
|
-
return getCurrentClient().account.getBalances(accountId);
|
|
101
|
-
},
|
|
102
|
-
getPosition(input) {
|
|
103
|
-
return getCurrentClient().account.getPosition(input);
|
|
104
|
-
},
|
|
105
|
-
getPositions(accountId) {
|
|
106
|
-
return getCurrentClient().account.getPositions(accountId);
|
|
107
|
-
},
|
|
108
|
-
getAccountSnapshot(accountId) {
|
|
109
|
-
return getCurrentClient().account.getAccountSnapshot(accountId);
|
|
110
|
-
},
|
|
111
|
-
getAccountStatus(accountId) {
|
|
112
|
-
return getCurrentClient().account.getAccountStatus(accountId);
|
|
113
|
-
},
|
|
114
|
-
getRiskSnapshot(accountId) {
|
|
115
|
-
return getCurrentClient().account.getRiskSnapshot(accountId);
|
|
116
|
-
},
|
|
117
|
-
watchAccountEvents() {
|
|
118
|
-
return accountEvents;
|
|
119
|
-
},
|
|
120
|
-
};
|
|
121
|
-
const order = {
|
|
122
|
-
async subscribeOrders(input) {
|
|
123
|
-
getRegisteredAccount(input.accountId);
|
|
124
|
-
const client = await requireStartedClient();
|
|
125
|
-
await client.order.subscribeOrders(input);
|
|
126
|
-
syncHealth();
|
|
127
|
-
},
|
|
128
|
-
async placeOrder(input) {
|
|
129
|
-
getRegisteredAccount(input.accountId);
|
|
130
|
-
assertSupportedExchange(input.exchange);
|
|
131
|
-
const client = await requireStartedClient();
|
|
132
|
-
const ack = await client.order.placeOrder(input);
|
|
133
|
-
syncHealth();
|
|
134
|
-
return ack;
|
|
135
|
-
},
|
|
136
|
-
async amendOrder(input) {
|
|
137
|
-
getRegisteredAccount(input.accountId);
|
|
138
|
-
assertSupportedExchange(input.exchange);
|
|
139
|
-
const client = await requireStartedClient();
|
|
140
|
-
const ack = await client.order.amendOrder(input);
|
|
141
|
-
syncHealth();
|
|
142
|
-
return ack;
|
|
143
|
-
},
|
|
144
|
-
async cancelOrder(input) {
|
|
145
|
-
getRegisteredAccount(input.accountId);
|
|
146
|
-
assertSupportedExchange(input.exchange);
|
|
147
|
-
const client = await requireStartedClient();
|
|
148
|
-
const ack = await client.order.cancelOrder(input);
|
|
149
|
-
syncHealth();
|
|
150
|
-
return ack;
|
|
151
|
-
},
|
|
152
|
-
async cancelAllOrders(input) {
|
|
153
|
-
getRegisteredAccount(input.accountId);
|
|
154
|
-
assertSupportedExchange(input.exchange);
|
|
155
|
-
const result = await requireStartedClient().then((client) => client.order.cancelAllOrders(input));
|
|
156
|
-
syncHealth();
|
|
157
|
-
return result;
|
|
158
|
-
},
|
|
159
|
-
getOrder(input) {
|
|
160
|
-
return getCurrentClient().order.getOrder(input);
|
|
161
|
-
},
|
|
162
|
-
getOpenOrders(accountId) {
|
|
163
|
-
return getCurrentClient().order.getOpenOrders(accountId);
|
|
164
|
-
},
|
|
165
|
-
getOrderStatus(accountId) {
|
|
166
|
-
return getCurrentClient().order.getOrderStatus(accountId);
|
|
167
|
-
},
|
|
168
|
-
};
|
|
169
|
-
function getCurrentClient() {
|
|
170
|
-
return (activeClient ?? {
|
|
171
|
-
market: idleMarket,
|
|
172
|
-
account: idleAccount,
|
|
173
|
-
order: idleOrder,
|
|
174
|
-
});
|
|
175
|
-
}
|
|
176
|
-
function bridgeEvents(source, push) {
|
|
177
|
-
void (async () => {
|
|
178
|
-
try {
|
|
179
|
-
for await (const value of source) {
|
|
180
|
-
push(value);
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
catch { }
|
|
184
|
-
})();
|
|
185
|
-
}
|
|
186
|
-
function attachClientBridges(client) {
|
|
187
|
-
bridgeEvents(client.account.watchAccountEvents(), (event) => {
|
|
188
|
-
accountEvents.push(event);
|
|
189
|
-
});
|
|
190
|
-
bridgeEvents(client.watchErrors(), (event) => {
|
|
191
|
-
errors.push(event);
|
|
192
|
-
});
|
|
193
|
-
bridgeEvents(client.watchHealth(), (event) => {
|
|
194
|
-
healthEvents.push(event);
|
|
195
|
-
});
|
|
196
|
-
}
|
|
197
|
-
function getRegisteredAccount(accountId) {
|
|
198
|
-
const account = accounts.get(accountId);
|
|
199
|
-
if (account === undefined) {
|
|
200
|
-
throw createAcexError({
|
|
201
|
-
code: "ACCOUNT_NOT_FOUND",
|
|
202
|
-
message: `account not found: ${accountId}`,
|
|
203
|
-
retryable: false,
|
|
204
|
-
accountId,
|
|
205
|
-
});
|
|
206
|
-
}
|
|
207
|
-
return account;
|
|
208
|
-
}
|
|
209
|
-
function getOnlyRegisteredAccount() {
|
|
210
|
-
const registered = [...accounts.values()][0];
|
|
211
|
-
if (registered === undefined) {
|
|
212
|
-
throw createAcexError({
|
|
213
|
-
code: "VALIDATION_ERROR",
|
|
214
|
-
message: "registerAccount() must be called before start()",
|
|
215
|
-
retryable: false,
|
|
216
|
-
});
|
|
217
|
-
}
|
|
218
|
-
return registered;
|
|
219
|
-
}
|
|
220
|
-
function assertNonEmpty(value, field) {
|
|
221
|
-
if (value.trim().length === 0) {
|
|
222
|
-
throw createAcexError({
|
|
223
|
-
code: "VALIDATION_ERROR",
|
|
224
|
-
message: `missing required field: ${field}`,
|
|
225
|
-
retryable: false,
|
|
226
|
-
});
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
function assertSupportedExchange(exchange) {
|
|
230
|
-
if (exchange !== PUBLIC_EXCHANGE) {
|
|
231
|
-
throw createAcexError({
|
|
232
|
-
code: "EXCHANGE_NOT_SUPPORTED",
|
|
233
|
-
message: `unsupported exchange: ${exchange}`,
|
|
234
|
-
retryable: false,
|
|
235
|
-
exchange,
|
|
236
|
-
});
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
function syncHealth() {
|
|
240
|
-
healthStore.setClientStatus(status);
|
|
241
|
-
if (activeAdapter !== undefined) {
|
|
242
|
-
healthStore.setExchangeHealth(activeAdapter.exchange, activeAdapter.getHealth());
|
|
243
|
-
}
|
|
244
|
-
if (activeClient === undefined) {
|
|
245
|
-
return;
|
|
246
|
-
}
|
|
247
|
-
for (const accountId of accounts.keys()) {
|
|
248
|
-
const accountStatus = activeClient.account.getAccountStatus(accountId);
|
|
249
|
-
if (accountStatus !== undefined) {
|
|
250
|
-
healthStore.setAccountStatus(accountId, accountStatus);
|
|
251
|
-
}
|
|
252
|
-
const orderStatus = activeClient.order.getOrderStatus(accountId);
|
|
253
|
-
if (orderStatus !== undefined) {
|
|
254
|
-
healthStore.setOrderStatus(accountId, orderStatus);
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
async function ensureActiveClient() {
|
|
259
|
-
if (activeClient !== undefined) {
|
|
260
|
-
return activeClient;
|
|
261
|
-
}
|
|
262
|
-
const registered = getOnlyRegisteredAccount();
|
|
263
|
-
try {
|
|
264
|
-
const exchangeInput = {
|
|
265
|
-
apiKey: registered.credentials.apiKey,
|
|
266
|
-
secret: registered.credentials.secret,
|
|
267
|
-
...(options.sandbox === undefined ? {} : { sandbox: options.sandbox }),
|
|
268
|
-
};
|
|
269
|
-
const exchange = dependencies.createExchange?.(exchangeInput) ?? createBinanceUsdmExchange(exchangeInput);
|
|
270
|
-
activeAdapter =
|
|
271
|
-
dependencies.createAdapter?.({
|
|
272
|
-
accountId: registered.accountId,
|
|
273
|
-
exchange,
|
|
274
|
-
}) ??
|
|
275
|
-
new BinanceCompositeAdapter({
|
|
276
|
-
marketAdapter: new BinanceNativeMarketAdapter(),
|
|
277
|
-
privateAdapter: new CcxtBinanceUsdMAdapter({
|
|
278
|
-
accountId: registered.accountId,
|
|
279
|
-
exchange: exchange,
|
|
280
|
-
}),
|
|
281
|
-
});
|
|
282
|
-
activeClient = createInternalClient({ adapter: activeAdapter });
|
|
283
|
-
attachClientBridges(activeClient);
|
|
284
|
-
return activeClient;
|
|
285
|
-
}
|
|
286
|
-
catch (error) {
|
|
287
|
-
throw toClientError(error, {
|
|
288
|
-
exchange: registered.exchange,
|
|
289
|
-
accountId: registered.accountId,
|
|
290
|
-
});
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
async function requireStartedClient() {
|
|
294
|
-
if (status !== "running") {
|
|
295
|
-
throw createAcexError({
|
|
296
|
-
code: "VALIDATION_ERROR",
|
|
297
|
-
message: "client.start() must be called before using runtime APIs",
|
|
298
|
-
retryable: false,
|
|
299
|
-
});
|
|
300
|
-
}
|
|
301
|
-
return ensureActiveClient();
|
|
302
|
-
}
|
|
303
|
-
return {
|
|
304
|
-
market,
|
|
305
|
-
account,
|
|
306
|
-
order,
|
|
307
|
-
getStatus: () => status,
|
|
308
|
-
getHealth() {
|
|
309
|
-
syncHealth();
|
|
310
|
-
return healthStore.snapshot();
|
|
311
|
-
},
|
|
312
|
-
watchErrors() {
|
|
313
|
-
return errors;
|
|
314
|
-
},
|
|
315
|
-
watchHealth() {
|
|
316
|
-
return healthEvents;
|
|
317
|
-
},
|
|
318
|
-
async registerAccount(input) {
|
|
319
|
-
assertNonEmpty(input.accountId, "accountId");
|
|
320
|
-
assertNonEmpty(input.credentials.apiKey, "credentials.apiKey");
|
|
321
|
-
assertNonEmpty(input.credentials.secret, "credentials.secret");
|
|
322
|
-
assertSupportedExchange(input.exchange);
|
|
323
|
-
if (accounts.has(input.accountId)) {
|
|
324
|
-
throw createAcexError({
|
|
325
|
-
code: "VALIDATION_ERROR",
|
|
326
|
-
message: `duplicate accountId: ${input.accountId}`,
|
|
327
|
-
retryable: false,
|
|
328
|
-
accountId: input.accountId,
|
|
329
|
-
exchange: input.exchange,
|
|
330
|
-
});
|
|
331
|
-
}
|
|
332
|
-
if (accounts.size > 0) {
|
|
333
|
-
throw createAcexError({
|
|
334
|
-
code: "CAPABILITY_NOT_SUPPORTED",
|
|
335
|
-
message: "public createClient() currently supports one registered account",
|
|
336
|
-
retryable: false,
|
|
337
|
-
exchange: input.exchange,
|
|
338
|
-
});
|
|
339
|
-
}
|
|
340
|
-
accounts.set(input.accountId, input);
|
|
341
|
-
if (status === "running" && activeClient === undefined) {
|
|
342
|
-
const client = await ensureActiveClient();
|
|
343
|
-
await client.start();
|
|
344
|
-
}
|
|
345
|
-
syncHealth();
|
|
346
|
-
return {
|
|
347
|
-
accountId: input.accountId,
|
|
348
|
-
exchange: input.exchange,
|
|
349
|
-
};
|
|
350
|
-
},
|
|
351
|
-
async start() {
|
|
352
|
-
if (status === "running") {
|
|
353
|
-
return;
|
|
354
|
-
}
|
|
355
|
-
const registered = getOnlyRegisteredAccount();
|
|
356
|
-
status = "starting";
|
|
357
|
-
syncHealth();
|
|
358
|
-
try {
|
|
359
|
-
const client = await ensureActiveClient();
|
|
360
|
-
await client.start();
|
|
361
|
-
status = "running";
|
|
362
|
-
syncHealth();
|
|
363
|
-
}
|
|
364
|
-
catch (error) {
|
|
365
|
-
status = "idle";
|
|
366
|
-
syncHealth();
|
|
367
|
-
throw toClientError(error, {
|
|
368
|
-
exchange: registered.exchange,
|
|
369
|
-
accountId: registered.accountId,
|
|
370
|
-
});
|
|
371
|
-
}
|
|
372
|
-
},
|
|
373
|
-
async stop() {
|
|
374
|
-
if (status === "stopped") {
|
|
375
|
-
return;
|
|
376
|
-
}
|
|
377
|
-
status = "stopping";
|
|
378
|
-
syncHealth();
|
|
379
|
-
await activeClient?.stop();
|
|
380
|
-
status = "stopped";
|
|
381
|
-
syncHealth();
|
|
382
|
-
},
|
|
383
|
-
};
|
|
384
|
-
}
|
|
385
|
-
export function createClient(options = {}) {
|
|
386
|
-
return createPublicClient(options);
|
|
387
|
-
}
|
|
388
|
-
function toClientError(error, context) {
|
|
389
|
-
if (isAcexError(error)) {
|
|
390
|
-
return error;
|
|
391
|
-
}
|
|
392
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
393
|
-
return createAcexError({
|
|
394
|
-
code: message.includes("ccxt.pro.binanceusdm is not available")
|
|
395
|
-
? "CAPABILITY_NOT_SUPPORTED"
|
|
396
|
-
: "INTERNAL_ERROR",
|
|
397
|
-
message,
|
|
398
|
-
retryable: false,
|
|
399
|
-
...(context.exchange === undefined ? {} : { exchange: context.exchange }),
|
|
400
|
-
...(context.accountId === undefined ? {} : { accountId: context.accountId }),
|
|
401
|
-
cause: error,
|
|
402
|
-
});
|
|
403
|
-
}
|
package/dist/core/recovery.d.ts
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
export declare function markMarketStale(input: {
|
|
2
|
-
exchange: string;
|
|
3
|
-
symbol: string;
|
|
4
|
-
receivedAt: number;
|
|
5
|
-
}): {
|
|
6
|
-
exchange: string;
|
|
7
|
-
symbol: string;
|
|
8
|
-
freshness: "stale";
|
|
9
|
-
staleSince: number;
|
|
10
|
-
reason: "ws_disconnected";
|
|
11
|
-
};
|
|
12
|
-
export declare function markMarketReconciling(input: {
|
|
13
|
-
exchange: string;
|
|
14
|
-
symbol: string;
|
|
15
|
-
receivedAt: number;
|
|
16
|
-
}): {
|
|
17
|
-
exchange: string;
|
|
18
|
-
symbol: string;
|
|
19
|
-
freshness: "reconciling";
|
|
20
|
-
lastStreamReceivedAt: number;
|
|
21
|
-
reason: "reconciling";
|
|
22
|
-
};
|
package/dist/core/recovery.js
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
export function markMarketStale(input) {
|
|
2
|
-
return {
|
|
3
|
-
exchange: input.exchange,
|
|
4
|
-
symbol: input.symbol,
|
|
5
|
-
freshness: "stale",
|
|
6
|
-
staleSince: input.receivedAt,
|
|
7
|
-
reason: "ws_disconnected",
|
|
8
|
-
};
|
|
9
|
-
}
|
|
10
|
-
export function markMarketReconciling(input) {
|
|
11
|
-
return {
|
|
12
|
-
exchange: input.exchange,
|
|
13
|
-
symbol: input.symbol,
|
|
14
|
-
freshness: "reconciling",
|
|
15
|
-
lastStreamReceivedAt: input.receivedAt,
|
|
16
|
-
reason: "reconciling",
|
|
17
|
-
};
|
|
18
|
-
}
|
package/dist/core/runtime.d.ts
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import type { ExchangeAdapter } from "../adapters/types.js";
|
|
2
|
-
import { type AccountManagerRuntime } from "../managers/account-manager.js";
|
|
3
|
-
import { createMarketManager } from "../managers/market-manager.js";
|
|
4
|
-
import { createOrderManager } from "../managers/order-manager.js";
|
|
5
|
-
import { markMarketReconciling, markMarketStale } from "./recovery.js";
|
|
6
|
-
export interface HealthEvent {
|
|
7
|
-
type: "market.status_changed";
|
|
8
|
-
exchange: string;
|
|
9
|
-
symbol: string;
|
|
10
|
-
status: ReturnType<typeof markMarketStale> | ReturnType<typeof markMarketReconciling>;
|
|
11
|
-
ts: number;
|
|
12
|
-
}
|
|
13
|
-
export interface RuntimeErrorEvent {
|
|
14
|
-
source: "adapter";
|
|
15
|
-
exchange: string;
|
|
16
|
-
error: Error;
|
|
17
|
-
ts: number;
|
|
18
|
-
}
|
|
19
|
-
export interface Runtime {
|
|
20
|
-
account: AccountManagerRuntime;
|
|
21
|
-
market: ReturnType<typeof createMarketManager>;
|
|
22
|
-
order: ReturnType<typeof createOrderManager>;
|
|
23
|
-
watchErrors(): AsyncIterable<RuntimeErrorEvent>;
|
|
24
|
-
watchHealth(): AsyncIterable<HealthEvent>;
|
|
25
|
-
}
|
|
26
|
-
export declare function createRuntime(adapter: ExchangeAdapter): Runtime;
|
package/dist/core/runtime.js
DELETED
|
@@ -1,150 +0,0 @@
|
|
|
1
|
-
import { createAccountManager } from "../managers/account-manager.js";
|
|
2
|
-
import { createMarketManager } from "../managers/market-manager.js";
|
|
3
|
-
import { createOrderManager } from "../managers/order-manager.js";
|
|
4
|
-
import { createAsyncQueue } from "../runtime/async-queue.js";
|
|
5
|
-
import { createAccountStore } from "../store/account-store.js";
|
|
6
|
-
import { createMarketStore } from "../store/market-store.js";
|
|
7
|
-
import { createOrderStore } from "../store/order-store.js";
|
|
8
|
-
import { markMarketReconciling, markMarketStale } from "./recovery.js";
|
|
9
|
-
export function createRuntime(adapter) {
|
|
10
|
-
const accountStore = createAccountStore();
|
|
11
|
-
const marketStore = createMarketStore();
|
|
12
|
-
const orderStore = createOrderStore();
|
|
13
|
-
const account = createAccountManager(accountStore, adapter);
|
|
14
|
-
const healthQueue = createAsyncQueue({ maxBufferSize: 100 });
|
|
15
|
-
const errorQueue = createAsyncQueue({ maxBufferSize: 100 });
|
|
16
|
-
if (adapter.setMarketEventSink !== undefined) {
|
|
17
|
-
adapter.setMarketEventSink((event) => {
|
|
18
|
-
applyMarketEvent(marketStore, event, healthQueue);
|
|
19
|
-
});
|
|
20
|
-
}
|
|
21
|
-
else {
|
|
22
|
-
void (async () => {
|
|
23
|
-
for await (const event of adapter.watchMarketEvents()) {
|
|
24
|
-
applyMarketEvent(marketStore, event, healthQueue);
|
|
25
|
-
}
|
|
26
|
-
})();
|
|
27
|
-
}
|
|
28
|
-
if (adapter.setOrderEventSink !== undefined) {
|
|
29
|
-
adapter.setOrderEventSink((event) => {
|
|
30
|
-
applyOrderEvent(orderStore, event);
|
|
31
|
-
});
|
|
32
|
-
}
|
|
33
|
-
else {
|
|
34
|
-
void (async () => {
|
|
35
|
-
for await (const event of adapter.watchOrderEvents()) {
|
|
36
|
-
applyOrderEvent(orderStore, event);
|
|
37
|
-
}
|
|
38
|
-
})();
|
|
39
|
-
}
|
|
40
|
-
if (adapter.setAccountEventSink !== undefined) {
|
|
41
|
-
adapter.setAccountEventSink((event) => {
|
|
42
|
-
applyAccountEvent(accountStore, event);
|
|
43
|
-
account.pushEvent(event);
|
|
44
|
-
});
|
|
45
|
-
}
|
|
46
|
-
else {
|
|
47
|
-
void (async () => {
|
|
48
|
-
for await (const event of adapter.watchAccountEvents()) {
|
|
49
|
-
applyAccountEvent(accountStore, event);
|
|
50
|
-
account.pushEvent(event);
|
|
51
|
-
}
|
|
52
|
-
})();
|
|
53
|
-
}
|
|
54
|
-
void (async () => {
|
|
55
|
-
for await (const error of adapter.watchInternalErrors()) {
|
|
56
|
-
errorQueue.push({
|
|
57
|
-
source: "adapter",
|
|
58
|
-
exchange: adapter.exchange,
|
|
59
|
-
error,
|
|
60
|
-
ts: Date.now(),
|
|
61
|
-
});
|
|
62
|
-
}
|
|
63
|
-
})();
|
|
64
|
-
return {
|
|
65
|
-
account,
|
|
66
|
-
market: createMarketManager(marketStore, adapter),
|
|
67
|
-
order: createOrderManager(orderStore, adapter),
|
|
68
|
-
watchErrors() {
|
|
69
|
-
return errorQueue;
|
|
70
|
-
},
|
|
71
|
-
watchHealth() {
|
|
72
|
-
return healthQueue;
|
|
73
|
-
},
|
|
74
|
-
};
|
|
75
|
-
}
|
|
76
|
-
function applyMarketEvent(marketStore, event, healthQueue) {
|
|
77
|
-
if (event.type === "market.disconnected") {
|
|
78
|
-
const status = markMarketStale(event);
|
|
79
|
-
marketStore.setStatus(status);
|
|
80
|
-
healthQueue.push({
|
|
81
|
-
type: "market.status_changed",
|
|
82
|
-
exchange: event.exchange,
|
|
83
|
-
symbol: event.symbol,
|
|
84
|
-
status,
|
|
85
|
-
ts: event.receivedAt,
|
|
86
|
-
});
|
|
87
|
-
return;
|
|
88
|
-
}
|
|
89
|
-
if (event.type === "market.reconnecting") {
|
|
90
|
-
const status = markMarketReconciling(event);
|
|
91
|
-
marketStore.setStatus(status);
|
|
92
|
-
healthQueue.push({
|
|
93
|
-
type: "market.status_changed",
|
|
94
|
-
exchange: event.exchange,
|
|
95
|
-
symbol: event.symbol,
|
|
96
|
-
status,
|
|
97
|
-
ts: event.receivedAt,
|
|
98
|
-
});
|
|
99
|
-
return;
|
|
100
|
-
}
|
|
101
|
-
if (event.type === "l1_book.updated") {
|
|
102
|
-
marketStore.upsertL1Book(event);
|
|
103
|
-
marketStore.setStatus({
|
|
104
|
-
exchange: event.exchange,
|
|
105
|
-
symbol: event.symbol,
|
|
106
|
-
freshness: "fresh",
|
|
107
|
-
lastStreamReceivedAt: event.receivedAt,
|
|
108
|
-
});
|
|
109
|
-
}
|
|
110
|
-
if (event.type === "funding_rate.updated") {
|
|
111
|
-
marketStore.upsertFundingRate(event);
|
|
112
|
-
marketStore.setStatus({
|
|
113
|
-
exchange: event.exchange,
|
|
114
|
-
symbol: event.symbol,
|
|
115
|
-
freshness: "fresh",
|
|
116
|
-
lastStreamReceivedAt: event.receivedAt,
|
|
117
|
-
});
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
function applyOrderEvent(orderStore, event) {
|
|
121
|
-
orderStore.upsertOrder(event.snapshot);
|
|
122
|
-
orderStore.setStatus({
|
|
123
|
-
accountId: event.accountId,
|
|
124
|
-
exchange: event.exchange,
|
|
125
|
-
status: "healthy",
|
|
126
|
-
bootstrapCompleted: true,
|
|
127
|
-
});
|
|
128
|
-
}
|
|
129
|
-
function applyAccountEvent(accountStore, event) {
|
|
130
|
-
const current = accountStore.getSnapshot(event.accountId);
|
|
131
|
-
const balances = {
|
|
132
|
-
...(current?.balances ?? {}),
|
|
133
|
-
[event.asset]: event.snapshot,
|
|
134
|
-
};
|
|
135
|
-
accountStore.replaceSnapshot({
|
|
136
|
-
accountId: event.accountId,
|
|
137
|
-
exchange: event.exchange,
|
|
138
|
-
balances,
|
|
139
|
-
positions: current?.positions ?? [],
|
|
140
|
-
...(current?.risk === undefined ? {} : { risk: current.risk }),
|
|
141
|
-
receivedAt: event.snapshot.receivedAt,
|
|
142
|
-
updatedAt: event.snapshot.updatedAt,
|
|
143
|
-
});
|
|
144
|
-
accountStore.setStatus({
|
|
145
|
-
accountId: event.accountId,
|
|
146
|
-
exchange: event.exchange,
|
|
147
|
-
status: "healthy",
|
|
148
|
-
bootstrapCompleted: true,
|
|
149
|
-
});
|
|
150
|
-
}
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import type { AcexErrorCode, Exchange } from "../types/public.js";
|
|
2
|
-
export interface AcexError extends Error {
|
|
3
|
-
name: "AcexError";
|
|
4
|
-
readonly code: AcexErrorCode;
|
|
5
|
-
readonly retryable: boolean;
|
|
6
|
-
readonly exchange?: Exchange;
|
|
7
|
-
readonly accountId?: string;
|
|
8
|
-
readonly requestId?: string;
|
|
9
|
-
readonly clientOrderId?: string;
|
|
10
|
-
readonly orderId?: string;
|
|
11
|
-
readonly cause?: unknown;
|
|
12
|
-
}
|
|
13
|
-
export interface CreateAcexErrorInput {
|
|
14
|
-
code: AcexErrorCode;
|
|
15
|
-
message: string;
|
|
16
|
-
retryable: boolean;
|
|
17
|
-
exchange?: Exchange;
|
|
18
|
-
accountId?: string;
|
|
19
|
-
requestId?: string;
|
|
20
|
-
clientOrderId?: string;
|
|
21
|
-
orderId?: string;
|
|
22
|
-
cause?: unknown;
|
|
23
|
-
}
|
|
24
|
-
export declare function createAcexError(input: CreateAcexErrorInput): AcexError;
|
|
25
|
-
export declare function isAcexError(value: unknown): value is AcexError;
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
const ACEX_ERROR_CODES = [
|
|
2
|
-
"VALIDATION_ERROR",
|
|
3
|
-
"EXCHANGE_NOT_SUPPORTED",
|
|
4
|
-
"ACCOUNT_NOT_FOUND",
|
|
5
|
-
"AUTH_FAILED",
|
|
6
|
-
"RATE_LIMITED",
|
|
7
|
-
"TRANSPORT_UNAVAILABLE",
|
|
8
|
-
"ORDER_REJECTED",
|
|
9
|
-
"INSUFFICIENT_BALANCE",
|
|
10
|
-
"REQUEST_OUTCOME_UNKNOWN",
|
|
11
|
-
"ORDER_STATE_UNKNOWN",
|
|
12
|
-
"EVENT_CONSUMER_OVERFLOW",
|
|
13
|
-
"CAPABILITY_NOT_SUPPORTED",
|
|
14
|
-
"INTERNAL_ERROR",
|
|
15
|
-
];
|
|
16
|
-
const ACEX_ERROR_CODE_SET = new Set(ACEX_ERROR_CODES);
|
|
17
|
-
export function createAcexError(input) {
|
|
18
|
-
const error = new Error(input.message, {
|
|
19
|
-
cause: input.cause,
|
|
20
|
-
});
|
|
21
|
-
error.name = "AcexError";
|
|
22
|
-
error.code = input.code;
|
|
23
|
-
error.retryable = input.retryable;
|
|
24
|
-
if (input.exchange !== undefined) {
|
|
25
|
-
error.exchange = input.exchange;
|
|
26
|
-
}
|
|
27
|
-
if (input.accountId !== undefined) {
|
|
28
|
-
error.accountId = input.accountId;
|
|
29
|
-
}
|
|
30
|
-
if (input.requestId !== undefined) {
|
|
31
|
-
error.requestId = input.requestId;
|
|
32
|
-
}
|
|
33
|
-
if (input.clientOrderId !== undefined) {
|
|
34
|
-
error.clientOrderId = input.clientOrderId;
|
|
35
|
-
}
|
|
36
|
-
if (input.orderId !== undefined) {
|
|
37
|
-
error.orderId = input.orderId;
|
|
38
|
-
}
|
|
39
|
-
if (input.cause !== undefined) {
|
|
40
|
-
error.cause = input.cause;
|
|
41
|
-
}
|
|
42
|
-
return error;
|
|
43
|
-
}
|
|
44
|
-
export function isAcexError(value) {
|
|
45
|
-
if (!(value instanceof Error)) {
|
|
46
|
-
return false;
|
|
47
|
-
}
|
|
48
|
-
if (!("code" in value) || !("retryable" in value)) {
|
|
49
|
-
return false;
|
|
50
|
-
}
|
|
51
|
-
const code = value.code;
|
|
52
|
-
const retryable = value.retryable;
|
|
53
|
-
return (typeof code === "string" && ACEX_ERROR_CODE_SET.has(code) && typeof retryable === "boolean");
|
|
54
|
-
}
|