@imbingox/acex 0.1.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 +65 -0
- package/dist/adapters/ccxt/aster-ccxt-adapter.d.ts +157 -0
- package/dist/adapters/ccxt/aster-ccxt-adapter.js +272 -0
- package/dist/adapters/ccxt/binance-usdm-ccxt-adapter.d.ts +179 -0
- package/dist/adapters/ccxt/binance-usdm-ccxt-adapter.js +537 -0
- package/dist/adapters/fake/fake-aster-adapter.d.ts +130 -0
- package/dist/adapters/fake/fake-aster-adapter.js +283 -0
- package/dist/adapters/types.d.ts +210 -0
- package/dist/adapters/types.js +1 -0
- package/dist/core/client.d.ts +37 -0
- package/dist/core/client.js +45 -0
- package/dist/core/recovery.d.ts +22 -0
- package/dist/core/recovery.js +18 -0
- package/dist/core/runtime.d.ts +26 -0
- package/dist/core/runtime.js +150 -0
- package/dist/errors/acex-error.d.ts +25 -0
- package/dist/errors/acex-error.js +54 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +3 -0
- package/dist/managers/account-manager.d.ts +41 -0
- package/dist/managers/account-manager.js +80 -0
- package/dist/managers/market-manager.d.ts +16 -0
- package/dist/managers/market-manager.js +28 -0
- package/dist/managers/order-manager.d.ts +87 -0
- package/dist/managers/order-manager.js +122 -0
- package/dist/runtime/async-queue.d.ts +8 -0
- package/dist/runtime/async-queue.js +88 -0
- package/dist/runtime/request-id.d.ts +1 -0
- package/dist/runtime/request-id.js +5 -0
- package/dist/store/account-store.d.ts +52 -0
- package/dist/store/account-store.js +18 -0
- package/dist/store/health-store.d.ts +16 -0
- package/dist/store/health-store.js +29 -0
- package/dist/store/market-store.d.ts +42 -0
- package/dist/store/market-store.js +51 -0
- package/dist/store/order-store.d.ts +38 -0
- package/dist/store/order-store.js +49 -0
- package/dist/testing/create-fake-runtime.d.ts +5 -0
- package/dist/testing/create-fake-runtime.js +7 -0
- package/dist/types/public.d.ts +11 -0
- package/dist/types/public.js +1 -0
- package/package.json +34 -0
|
@@ -0,0 +1,150 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
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;
|
|
@@ -0,0 +1,54 @@
|
|
|
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
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { createClient } from "./core/client.js";
|
|
2
|
+
export { createAcexError, isAcexError } from "./errors/acex-error.js";
|
|
3
|
+
export { IMPLEMENTED_EXCHANGES } from "./types/public.js";
|
|
4
|
+
export type { AcexErrorCode, ClientStatus, Exchange, ImplementedExchange, Logger, } from "./types/public.js";
|
|
5
|
+
export type { AcexError, CreateAcexErrorInput } from "./errors/acex-error.js";
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { AdapterAccountBaseline, NormalizedAccountEvent } from "../adapters/types.js";
|
|
2
|
+
import type { AccountBalanceSnapshot, AccountPositionSnapshot, AccountRiskSnapshot, AccountSnapshot, AccountStatus, AccountStore } from "../store/account-store.js";
|
|
3
|
+
export interface SubscribeAccountInput {
|
|
4
|
+
accountId: string;
|
|
5
|
+
}
|
|
6
|
+
export interface AccountPositionLookupInput {
|
|
7
|
+
accountId: string;
|
|
8
|
+
symbol: string;
|
|
9
|
+
}
|
|
10
|
+
export interface AccountManager {
|
|
11
|
+
subscribeAccount(input: SubscribeAccountInput): Promise<void>;
|
|
12
|
+
getBalance(accountId: string, asset: string): AccountBalanceSnapshot | undefined;
|
|
13
|
+
getBalances(accountId: string): AccountBalanceSnapshot[];
|
|
14
|
+
getPosition(input: AccountPositionLookupInput): AccountPositionSnapshot | undefined;
|
|
15
|
+
getPositions(accountId: string): AccountPositionSnapshot[];
|
|
16
|
+
getAccountSnapshot(accountId: string): AccountSnapshot | undefined;
|
|
17
|
+
getAccountStatus(accountId: string): AccountStatus | undefined;
|
|
18
|
+
getRiskSnapshot(accountId: string): AccountRiskSnapshot | undefined;
|
|
19
|
+
watchAccountEvents(): AsyncIterable<NormalizedAccountEvent>;
|
|
20
|
+
}
|
|
21
|
+
interface AccountAdapter {
|
|
22
|
+
exchange: string;
|
|
23
|
+
fetchAccountBaseline(accountId: string): Promise<AdapterAccountBaseline>;
|
|
24
|
+
}
|
|
25
|
+
export declare function createAccountManager(store: AccountStore, adapter: AccountAdapter): {
|
|
26
|
+
subscribeAccount(input: SubscribeAccountInput): Promise<void>;
|
|
27
|
+
getBalance(accountId: string, asset: string): AccountBalanceSnapshot | undefined;
|
|
28
|
+
getBalances(accountId: string): AccountBalanceSnapshot[];
|
|
29
|
+
getPosition(input: AccountPositionLookupInput): AccountPositionSnapshot | undefined;
|
|
30
|
+
getPositions(accountId: string): AccountPositionSnapshot[];
|
|
31
|
+
getAccountSnapshot: (accountId: string) => AccountSnapshot | undefined;
|
|
32
|
+
getAccountStatus: (accountId: string) => AccountStatus | undefined;
|
|
33
|
+
getRiskSnapshot(accountId: string): AccountRiskSnapshot | undefined;
|
|
34
|
+
watchAccountEvents(): import("../runtime/async-queue.js").AsyncQueue<NormalizedAccountEvent>;
|
|
35
|
+
pushEvent(event: NormalizedAccountEvent): void;
|
|
36
|
+
};
|
|
37
|
+
export declare function createIdleAccountManager(): AccountManager;
|
|
38
|
+
export interface AccountManagerRuntime extends AccountManager {
|
|
39
|
+
pushEvent(event: NormalizedAccountEvent): void;
|
|
40
|
+
}
|
|
41
|
+
export {};
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { createAsyncQueue } from "../runtime/async-queue.js";
|
|
2
|
+
export function createAccountManager(store, adapter) {
|
|
3
|
+
const publicEvents = createAsyncQueue({ maxBufferSize: 100 });
|
|
4
|
+
return {
|
|
5
|
+
async subscribeAccount(input) {
|
|
6
|
+
const baseline = await adapter.fetchAccountBaseline(input.accountId);
|
|
7
|
+
const now = Date.now();
|
|
8
|
+
store.replaceSnapshot({
|
|
9
|
+
accountId: input.accountId,
|
|
10
|
+
exchange: adapter.exchange,
|
|
11
|
+
balances: baseline.balances,
|
|
12
|
+
positions: baseline.positions,
|
|
13
|
+
...(baseline.risk === undefined ? {} : { risk: baseline.risk }),
|
|
14
|
+
receivedAt: now,
|
|
15
|
+
updatedAt: now,
|
|
16
|
+
});
|
|
17
|
+
store.setStatus({
|
|
18
|
+
accountId: input.accountId,
|
|
19
|
+
exchange: adapter.exchange,
|
|
20
|
+
status: "healthy",
|
|
21
|
+
bootstrapCompleted: true,
|
|
22
|
+
});
|
|
23
|
+
},
|
|
24
|
+
getBalance(accountId, asset) {
|
|
25
|
+
return store.getSnapshot(accountId)?.balances?.[asset];
|
|
26
|
+
},
|
|
27
|
+
getBalances(accountId) {
|
|
28
|
+
return Object.values(store.getSnapshot(accountId)?.balances ?? {});
|
|
29
|
+
},
|
|
30
|
+
getPosition(input) {
|
|
31
|
+
return (store.getSnapshot(input.accountId)?.positions ?? []).find((position) => {
|
|
32
|
+
return position.symbol === input.symbol;
|
|
33
|
+
});
|
|
34
|
+
},
|
|
35
|
+
getPositions(accountId) {
|
|
36
|
+
return store.getSnapshot(accountId)?.positions ?? [];
|
|
37
|
+
},
|
|
38
|
+
getAccountSnapshot: store.getSnapshot,
|
|
39
|
+
getAccountStatus: store.getStatus,
|
|
40
|
+
getRiskSnapshot(accountId) {
|
|
41
|
+
return store.getSnapshot(accountId)?.risk;
|
|
42
|
+
},
|
|
43
|
+
watchAccountEvents() {
|
|
44
|
+
return publicEvents;
|
|
45
|
+
},
|
|
46
|
+
pushEvent(event) {
|
|
47
|
+
publicEvents.push(event);
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
export function createIdleAccountManager() {
|
|
52
|
+
const events = createAsyncQueue({ maxBufferSize: 100 });
|
|
53
|
+
return {
|
|
54
|
+
async subscribeAccount() { },
|
|
55
|
+
getBalance() {
|
|
56
|
+
return undefined;
|
|
57
|
+
},
|
|
58
|
+
getBalances() {
|
|
59
|
+
return [];
|
|
60
|
+
},
|
|
61
|
+
getPosition() {
|
|
62
|
+
return undefined;
|
|
63
|
+
},
|
|
64
|
+
getPositions() {
|
|
65
|
+
return [];
|
|
66
|
+
},
|
|
67
|
+
getAccountSnapshot() {
|
|
68
|
+
return undefined;
|
|
69
|
+
},
|
|
70
|
+
getAccountStatus() {
|
|
71
|
+
return undefined;
|
|
72
|
+
},
|
|
73
|
+
getRiskSnapshot() {
|
|
74
|
+
return undefined;
|
|
75
|
+
},
|
|
76
|
+
watchAccountEvents() {
|
|
77
|
+
return events;
|
|
78
|
+
},
|
|
79
|
+
};
|
|
80
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { MarketDataStatus, MarketStore, MarketStoreKey } from "../store/market-store.js";
|
|
2
|
+
export interface MarketManager {
|
|
3
|
+
subscribeL1Book(input: MarketStoreKey): Promise<void>;
|
|
4
|
+
subscribeFundingRate(input: MarketStoreKey): Promise<void>;
|
|
5
|
+
getL1Book(key: MarketStoreKey): ReturnType<MarketStore["getL1Book"]>;
|
|
6
|
+
getFundingRate(key: MarketStoreKey): ReturnType<MarketStore["getFundingRate"]>;
|
|
7
|
+
getMarketSnapshot(key: MarketStoreKey): ReturnType<MarketStore["getSnapshot"]>;
|
|
8
|
+
getMarketStatus(key: MarketStoreKey): MarketDataStatus | undefined;
|
|
9
|
+
}
|
|
10
|
+
interface MarketSubscriptionAdapter {
|
|
11
|
+
subscribeL1Book(input: MarketStoreKey): Promise<void>;
|
|
12
|
+
subscribeFundingRate(input: MarketStoreKey): Promise<void>;
|
|
13
|
+
}
|
|
14
|
+
export declare function createMarketManager(store: MarketStore, adapter: MarketSubscriptionAdapter): MarketManager;
|
|
15
|
+
export declare function createIdleMarketManager(): MarketManager;
|
|
16
|
+
export {};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export function createMarketManager(store, adapter) {
|
|
2
|
+
return {
|
|
3
|
+
subscribeL1Book: adapter.subscribeL1Book.bind(adapter),
|
|
4
|
+
subscribeFundingRate: adapter.subscribeFundingRate.bind(adapter),
|
|
5
|
+
getL1Book: store.getL1Book,
|
|
6
|
+
getFundingRate: store.getFundingRate,
|
|
7
|
+
getMarketSnapshot: store.getSnapshot,
|
|
8
|
+
getMarketStatus: store.getStatus,
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
export function createIdleMarketManager() {
|
|
12
|
+
return {
|
|
13
|
+
async subscribeL1Book() { },
|
|
14
|
+
async subscribeFundingRate() { },
|
|
15
|
+
getL1Book() {
|
|
16
|
+
return undefined;
|
|
17
|
+
},
|
|
18
|
+
getFundingRate() {
|
|
19
|
+
return undefined;
|
|
20
|
+
},
|
|
21
|
+
getMarketSnapshot() {
|
|
22
|
+
return {};
|
|
23
|
+
},
|
|
24
|
+
getMarketStatus() {
|
|
25
|
+
return undefined;
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import type { OrderSnapshot, OrderStatus, OrderStore } from "../store/order-store.js";
|
|
2
|
+
export interface SubscribeOrdersInput {
|
|
3
|
+
accountId: string;
|
|
4
|
+
}
|
|
5
|
+
export interface PlaceOrderInput {
|
|
6
|
+
accountId: string;
|
|
7
|
+
exchange: string;
|
|
8
|
+
symbol: string;
|
|
9
|
+
side: "buy" | "sell";
|
|
10
|
+
amount: string;
|
|
11
|
+
clientOrderId: string;
|
|
12
|
+
type: string;
|
|
13
|
+
price?: string;
|
|
14
|
+
reduceOnly?: boolean;
|
|
15
|
+
}
|
|
16
|
+
export interface AmendOrderInput {
|
|
17
|
+
accountId: string;
|
|
18
|
+
exchange: string;
|
|
19
|
+
clientOrderId?: string;
|
|
20
|
+
orderId?: string;
|
|
21
|
+
symbol?: string;
|
|
22
|
+
newPrice?: string;
|
|
23
|
+
}
|
|
24
|
+
export interface CancelOrderInput {
|
|
25
|
+
accountId: string;
|
|
26
|
+
exchange: string;
|
|
27
|
+
clientOrderId?: string;
|
|
28
|
+
orderId?: string;
|
|
29
|
+
}
|
|
30
|
+
export interface CancelAllOrdersInput {
|
|
31
|
+
accountId: string;
|
|
32
|
+
exchange: string;
|
|
33
|
+
}
|
|
34
|
+
export interface OrderLookupInput {
|
|
35
|
+
accountId: string;
|
|
36
|
+
exchange: string;
|
|
37
|
+
clientOrderId?: string;
|
|
38
|
+
orderId?: string;
|
|
39
|
+
}
|
|
40
|
+
export interface OrderAck {
|
|
41
|
+
requestId: string;
|
|
42
|
+
accountId: string;
|
|
43
|
+
exchange: string;
|
|
44
|
+
clientOrderId?: string;
|
|
45
|
+
orderId?: string;
|
|
46
|
+
symbol?: string;
|
|
47
|
+
submittedAt: number;
|
|
48
|
+
}
|
|
49
|
+
export interface CancelAllOrdersResult {
|
|
50
|
+
accountId: string;
|
|
51
|
+
exchange: string;
|
|
52
|
+
canceledCount: number;
|
|
53
|
+
}
|
|
54
|
+
export interface OrderManager {
|
|
55
|
+
subscribeOrders(input: SubscribeOrdersInput): Promise<void>;
|
|
56
|
+
placeOrder(input: PlaceOrderInput): Promise<OrderAck>;
|
|
57
|
+
amendOrder(input: AmendOrderInput): Promise<OrderAck>;
|
|
58
|
+
cancelOrder(input: CancelOrderInput): Promise<OrderAck>;
|
|
59
|
+
cancelAllOrders(input: CancelAllOrdersInput): Promise<CancelAllOrdersResult>;
|
|
60
|
+
getOrder(input: OrderLookupInput): OrderSnapshot | undefined;
|
|
61
|
+
getOpenOrders(accountId: string): OrderSnapshot[];
|
|
62
|
+
getOrderStatus(accountId: string): OrderStatus | undefined;
|
|
63
|
+
}
|
|
64
|
+
interface OrderAdapter {
|
|
65
|
+
exchange: string;
|
|
66
|
+
subscribeOrders(input: SubscribeOrdersInput): Promise<void>;
|
|
67
|
+
fetchOpenOrdersBaseline(accountId: string): Promise<OrderSnapshot[]>;
|
|
68
|
+
placeOrder(input: PlaceOrderInput): Promise<{
|
|
69
|
+
clientOrderId?: string;
|
|
70
|
+
orderId?: string;
|
|
71
|
+
receivedAt: number;
|
|
72
|
+
}>;
|
|
73
|
+
amendOrder(input: AmendOrderInput): Promise<{
|
|
74
|
+
clientOrderId?: string;
|
|
75
|
+
orderId?: string;
|
|
76
|
+
receivedAt: number;
|
|
77
|
+
}>;
|
|
78
|
+
cancelOrder(input: CancelOrderInput): Promise<{
|
|
79
|
+
clientOrderId?: string;
|
|
80
|
+
orderId?: string;
|
|
81
|
+
receivedAt: number;
|
|
82
|
+
}>;
|
|
83
|
+
cancelAllOrders(input: CancelAllOrdersInput): Promise<CancelAllOrdersResult>;
|
|
84
|
+
}
|
|
85
|
+
export declare function createOrderManager(store: OrderStore, adapter: OrderAdapter): OrderManager;
|
|
86
|
+
export declare function createIdleOrderManager(): OrderManager;
|
|
87
|
+
export {};
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { nextRequestId } from "../runtime/request-id.js";
|
|
2
|
+
export function createOrderManager(store, adapter) {
|
|
3
|
+
return {
|
|
4
|
+
async subscribeOrders(input) {
|
|
5
|
+
await adapter.subscribeOrders(input);
|
|
6
|
+
const baseline = await adapter.fetchOpenOrdersBaseline(input.accountId);
|
|
7
|
+
for (const snapshot of baseline) {
|
|
8
|
+
store.upsertOrder(snapshot);
|
|
9
|
+
}
|
|
10
|
+
store.setStatus({
|
|
11
|
+
accountId: input.accountId,
|
|
12
|
+
exchange: adapter.exchange,
|
|
13
|
+
status: "healthy",
|
|
14
|
+
bootstrapCompleted: true,
|
|
15
|
+
});
|
|
16
|
+
},
|
|
17
|
+
async placeOrder(input) {
|
|
18
|
+
const result = await adapter.placeOrder(input);
|
|
19
|
+
return createOrderAck({
|
|
20
|
+
requestId: nextRequestId(),
|
|
21
|
+
accountId: input.accountId,
|
|
22
|
+
exchange: input.exchange,
|
|
23
|
+
symbol: input.symbol,
|
|
24
|
+
clientOrderId: result.clientOrderId ?? input.clientOrderId,
|
|
25
|
+
orderId: result.orderId,
|
|
26
|
+
submittedAt: result.receivedAt,
|
|
27
|
+
});
|
|
28
|
+
},
|
|
29
|
+
async amendOrder(input) {
|
|
30
|
+
const result = await adapter.amendOrder(input);
|
|
31
|
+
return createOrderAck({
|
|
32
|
+
requestId: nextRequestId(),
|
|
33
|
+
accountId: input.accountId,
|
|
34
|
+
exchange: input.exchange,
|
|
35
|
+
symbol: input.symbol,
|
|
36
|
+
clientOrderId: result.clientOrderId ?? input.clientOrderId,
|
|
37
|
+
orderId: result.orderId,
|
|
38
|
+
submittedAt: result.receivedAt,
|
|
39
|
+
});
|
|
40
|
+
},
|
|
41
|
+
async cancelOrder(input) {
|
|
42
|
+
const result = await adapter.cancelOrder(input);
|
|
43
|
+
return createOrderAck({
|
|
44
|
+
requestId: nextRequestId(),
|
|
45
|
+
accountId: input.accountId,
|
|
46
|
+
exchange: input.exchange,
|
|
47
|
+
clientOrderId: result.clientOrderId ?? input.clientOrderId,
|
|
48
|
+
orderId: result.orderId,
|
|
49
|
+
submittedAt: result.receivedAt,
|
|
50
|
+
});
|
|
51
|
+
},
|
|
52
|
+
cancelAllOrders: adapter.cancelAllOrders.bind(adapter),
|
|
53
|
+
getOrder(input) {
|
|
54
|
+
return store.getOrder(input.accountId, input);
|
|
55
|
+
},
|
|
56
|
+
getOpenOrders: store.getOpenOrders,
|
|
57
|
+
getOrderStatus: store.getStatus,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
export function createIdleOrderManager() {
|
|
61
|
+
return {
|
|
62
|
+
async subscribeOrders() { },
|
|
63
|
+
async placeOrder(input) {
|
|
64
|
+
return createOrderAck({
|
|
65
|
+
requestId: nextRequestId(),
|
|
66
|
+
accountId: input.accountId,
|
|
67
|
+
exchange: input.exchange,
|
|
68
|
+
symbol: input.symbol,
|
|
69
|
+
clientOrderId: input.clientOrderId,
|
|
70
|
+
submittedAt: Date.now(),
|
|
71
|
+
});
|
|
72
|
+
},
|
|
73
|
+
async amendOrder(input) {
|
|
74
|
+
return createOrderAck({
|
|
75
|
+
requestId: nextRequestId(),
|
|
76
|
+
accountId: input.accountId,
|
|
77
|
+
exchange: input.exchange,
|
|
78
|
+
symbol: input.symbol,
|
|
79
|
+
clientOrderId: input.clientOrderId,
|
|
80
|
+
orderId: input.orderId,
|
|
81
|
+
submittedAt: Date.now(),
|
|
82
|
+
});
|
|
83
|
+
},
|
|
84
|
+
async cancelOrder(input) {
|
|
85
|
+
return createOrderAck({
|
|
86
|
+
requestId: nextRequestId(),
|
|
87
|
+
accountId: input.accountId,
|
|
88
|
+
exchange: input.exchange,
|
|
89
|
+
clientOrderId: input.clientOrderId,
|
|
90
|
+
orderId: input.orderId,
|
|
91
|
+
submittedAt: Date.now(),
|
|
92
|
+
});
|
|
93
|
+
},
|
|
94
|
+
async cancelAllOrders(input) {
|
|
95
|
+
return {
|
|
96
|
+
accountId: input.accountId,
|
|
97
|
+
exchange: input.exchange,
|
|
98
|
+
canceledCount: 0,
|
|
99
|
+
};
|
|
100
|
+
},
|
|
101
|
+
getOrder() {
|
|
102
|
+
return undefined;
|
|
103
|
+
},
|
|
104
|
+
getOpenOrders() {
|
|
105
|
+
return [];
|
|
106
|
+
},
|
|
107
|
+
getOrderStatus() {
|
|
108
|
+
return undefined;
|
|
109
|
+
},
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
function createOrderAck(input) {
|
|
113
|
+
return {
|
|
114
|
+
requestId: input.requestId,
|
|
115
|
+
accountId: input.accountId,
|
|
116
|
+
exchange: input.exchange,
|
|
117
|
+
submittedAt: input.submittedAt,
|
|
118
|
+
...(input.clientOrderId === undefined ? {} : { clientOrderId: input.clientOrderId }),
|
|
119
|
+
...(input.orderId === undefined ? {} : { orderId: input.orderId }),
|
|
120
|
+
...(input.symbol === undefined ? {} : { symbol: input.symbol }),
|
|
121
|
+
};
|
|
122
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export interface CreateAsyncQueueInput {
|
|
2
|
+
maxBufferSize: number;
|
|
3
|
+
}
|
|
4
|
+
export interface AsyncQueue<T> extends AsyncIterable<T> {
|
|
5
|
+
push(value: T): void;
|
|
6
|
+
close(): void;
|
|
7
|
+
}
|
|
8
|
+
export declare function createAsyncQueue<T>(input: CreateAsyncQueueInput): AsyncQueue<T>;
|