@imbingox/acex 0.1.0-beta.0 → 0.1.0-beta.1
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 +12 -54
- package/index.ts +1 -0
- package/package.json +15 -24
- package/src/adapters/binance/adapter.ts +53 -0
- package/src/adapters/binance/book-ticker.ts +123 -0
- package/src/adapters/binance/market-catalog.ts +251 -0
- package/src/adapters/types.ts +43 -0
- package/src/client/context.ts +60 -0
- package/src/client/create-client.ts +6 -0
- package/src/client/runtime.ts +283 -0
- package/src/errors.ts +20 -0
- package/src/index.ts +4 -0
- package/src/internal/async-event-bus.ts +100 -0
- package/src/internal/filters.ts +119 -0
- package/src/internal/managed-websocket.ts +258 -0
- package/src/managers/account-manager.ts +315 -0
- package/src/managers/market-manager.ts +642 -0
- package/src/managers/order-manager.ts +304 -0
- package/src/types/account.ts +160 -0
- package/src/types/client.ts +79 -0
- package/src/types/index.ts +5 -0
- package/src/types/market.ts +136 -0
- package/src/types/order.ts +142 -0
- package/src/types/shared.ts +78 -0
- 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 -179
- package/dist/adapters/ccxt/binance-usdm-ccxt-adapter.js +0 -537
- 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 -37
- package/dist/core/client.js +0 -45
- 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 -5
- 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/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 -11
- package/dist/types/public.js +0 -1
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import type { PositionSide } from "./account.ts";
|
|
2
|
+
import type {
|
|
3
|
+
Exchange,
|
|
4
|
+
PrivateRuntimeStatus,
|
|
5
|
+
SubscriptionActivity,
|
|
6
|
+
} from "./shared.ts";
|
|
7
|
+
|
|
8
|
+
export interface OrderDataStatus {
|
|
9
|
+
accountId: string;
|
|
10
|
+
exchange: Exchange;
|
|
11
|
+
activity: SubscriptionActivity;
|
|
12
|
+
ready: boolean;
|
|
13
|
+
runtimeStatus?: PrivateRuntimeStatus;
|
|
14
|
+
lastReceivedAt?: number;
|
|
15
|
+
lastReadyAt?: number;
|
|
16
|
+
inactiveSince?: number;
|
|
17
|
+
reason?:
|
|
18
|
+
| "credentials_missing"
|
|
19
|
+
| "auth_failed"
|
|
20
|
+
| "ws_disconnected"
|
|
21
|
+
| "heartbeat_timeout"
|
|
22
|
+
| "reconciling";
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface OrderStatusChangedEvent {
|
|
26
|
+
type: "order.status_changed";
|
|
27
|
+
accountId: string;
|
|
28
|
+
exchange: Exchange;
|
|
29
|
+
status: OrderDataStatus;
|
|
30
|
+
ts: number;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export type OrderSide = "buy" | "sell";
|
|
34
|
+
|
|
35
|
+
export type OrderStatus =
|
|
36
|
+
| "open"
|
|
37
|
+
| "partially_filled"
|
|
38
|
+
| "filled"
|
|
39
|
+
| "canceled"
|
|
40
|
+
| "rejected"
|
|
41
|
+
| "expired";
|
|
42
|
+
|
|
43
|
+
export interface SubscribeOrdersInput {
|
|
44
|
+
accountId: string;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface UnsubscribeOrdersInput {
|
|
48
|
+
accountId: string;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export interface GetOrderInput {
|
|
52
|
+
accountId: string;
|
|
53
|
+
orderId?: string;
|
|
54
|
+
clientOrderId?: string;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export interface OrderEventFilter {
|
|
58
|
+
accountId?: string;
|
|
59
|
+
exchange?: Exchange;
|
|
60
|
+
symbol?: string;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export interface OrderSnapshot {
|
|
64
|
+
accountId: string;
|
|
65
|
+
exchange: Exchange;
|
|
66
|
+
orderId?: string;
|
|
67
|
+
clientOrderId?: string;
|
|
68
|
+
symbol: string;
|
|
69
|
+
side: OrderSide;
|
|
70
|
+
type: string;
|
|
71
|
+
status: OrderStatus;
|
|
72
|
+
price?: string;
|
|
73
|
+
triggerPrice?: string;
|
|
74
|
+
amount: string;
|
|
75
|
+
filled: string;
|
|
76
|
+
remaining?: string;
|
|
77
|
+
reduceOnly?: boolean;
|
|
78
|
+
positionSide?: PositionSide;
|
|
79
|
+
avgFillPrice?: string;
|
|
80
|
+
exchangeTs?: number;
|
|
81
|
+
receivedAt: number;
|
|
82
|
+
updatedAt: number;
|
|
83
|
+
seq: number;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export interface OrderEventBase {
|
|
87
|
+
accountId: string;
|
|
88
|
+
exchange: Exchange;
|
|
89
|
+
symbol: string;
|
|
90
|
+
ts: number;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export interface OrderUpdatedEvent extends OrderEventBase {
|
|
94
|
+
type: "order.updated";
|
|
95
|
+
snapshot: OrderSnapshot;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export interface OrderFilledEvent extends OrderEventBase {
|
|
99
|
+
type: "order.filled";
|
|
100
|
+
snapshot: OrderSnapshot;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export interface OrderCanceledEvent extends OrderEventBase {
|
|
104
|
+
type: "order.canceled";
|
|
105
|
+
snapshot: OrderSnapshot;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export interface OrderRejectedEvent extends OrderEventBase {
|
|
109
|
+
type: "order.rejected";
|
|
110
|
+
snapshot: OrderSnapshot;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export interface OrderSnapshotReplacedEvent {
|
|
114
|
+
type: "order.snapshot_replaced";
|
|
115
|
+
accountId: string;
|
|
116
|
+
exchange: Exchange;
|
|
117
|
+
snapshot: OrderSnapshot[];
|
|
118
|
+
ts: number;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export type OrderEvent =
|
|
122
|
+
| OrderUpdatedEvent
|
|
123
|
+
| OrderFilledEvent
|
|
124
|
+
| OrderCanceledEvent
|
|
125
|
+
| OrderRejectedEvent
|
|
126
|
+
| OrderSnapshotReplacedEvent;
|
|
127
|
+
|
|
128
|
+
export interface OrderEventStreams {
|
|
129
|
+
updates(filter?: OrderEventFilter): AsyncIterable<OrderEvent>;
|
|
130
|
+
status(filter?: OrderEventFilter): AsyncIterable<OrderStatusChangedEvent>;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export interface OrderManager {
|
|
134
|
+
readonly events: OrderEventStreams;
|
|
135
|
+
|
|
136
|
+
subscribeOrders(input: SubscribeOrdersInput): Promise<void>;
|
|
137
|
+
unsubscribeOrders(input: UnsubscribeOrdersInput): Promise<void>;
|
|
138
|
+
|
|
139
|
+
getOrder(input: GetOrderInput): OrderSnapshot | undefined;
|
|
140
|
+
getOpenOrders(accountId: string, symbol?: string): OrderSnapshot[];
|
|
141
|
+
getOrderStatus(accountId: string): OrderDataStatus | undefined;
|
|
142
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
export const SUPPORTED_EXCHANGES = ["binance", "okx", "bybit", "gate"] as const;
|
|
2
|
+
|
|
3
|
+
export type Exchange = (typeof SUPPORTED_EXCHANGES)[number];
|
|
4
|
+
|
|
5
|
+
export type ClientStatus =
|
|
6
|
+
| "idle"
|
|
7
|
+
| "starting"
|
|
8
|
+
| "running"
|
|
9
|
+
| "stopping"
|
|
10
|
+
| "stopped";
|
|
11
|
+
|
|
12
|
+
export type LogLevel = "debug" | "info" | "warn" | "error";
|
|
13
|
+
|
|
14
|
+
export interface Logger {
|
|
15
|
+
debug(msg: string, context?: Record<string, unknown>): void;
|
|
16
|
+
info(msg: string, context?: Record<string, unknown>): void;
|
|
17
|
+
warn(msg: string, context?: Record<string, unknown>): void;
|
|
18
|
+
error(msg: string, context?: Record<string, unknown>): void;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface MarketRuntimeOptions {
|
|
22
|
+
l1InitialMessageTimeoutMs?: number;
|
|
23
|
+
l1StaleAfterMs?: number;
|
|
24
|
+
l1ReconnectDelayMs?: number;
|
|
25
|
+
l1ReconnectMaxDelayMs?: number;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface CreateClientOptions {
|
|
29
|
+
sandbox?: boolean;
|
|
30
|
+
logger?: Logger;
|
|
31
|
+
logLevel?: LogLevel;
|
|
32
|
+
market?: MarketRuntimeOptions;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface AccountCredentials {
|
|
36
|
+
apiKey?: string;
|
|
37
|
+
secret?: string;
|
|
38
|
+
password?: string;
|
|
39
|
+
extra?: Record<string, string>;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export interface RegisterAccountInput {
|
|
43
|
+
accountId: string;
|
|
44
|
+
exchange: Exchange;
|
|
45
|
+
credentials?: AccountCredentials;
|
|
46
|
+
options?: Record<string, unknown>;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export interface RegisterAccountResult {
|
|
50
|
+
accountId: string;
|
|
51
|
+
exchange: Exchange;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export interface StopOptions {
|
|
55
|
+
graceful?: boolean;
|
|
56
|
+
timeoutMs?: number;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export interface AcexInternalError {
|
|
60
|
+
source: "client" | "market" | "account" | "order" | "adapter" | "runtime";
|
|
61
|
+
exchange?: Exchange;
|
|
62
|
+
accountId?: string;
|
|
63
|
+
symbol?: string;
|
|
64
|
+
error: Error;
|
|
65
|
+
ts: number;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export type SubscriptionActivity = "active" | "inactive";
|
|
69
|
+
|
|
70
|
+
export type MarketFreshness = "fresh" | "stale" | "reconciling";
|
|
71
|
+
|
|
72
|
+
export type PrivateRuntimeStatus =
|
|
73
|
+
| "bootstrap_pending"
|
|
74
|
+
| "healthy"
|
|
75
|
+
| "degraded"
|
|
76
|
+
| "reconnecting"
|
|
77
|
+
| "reconciling"
|
|
78
|
+
| "stopped";
|
|
@@ -1,157 +0,0 @@
|
|
|
1
|
-
import type { AdapterAccountBaseline, ExchangeCapabilities, NormalizedAccountEvent, NormalizedMarketEvent, NormalizedOrderEvent } from "../types.js";
|
|
2
|
-
interface CcxtTickerLike {
|
|
3
|
-
symbol: string;
|
|
4
|
-
bid: number | string | undefined;
|
|
5
|
-
bidVolume: number | string | undefined;
|
|
6
|
-
ask: number | string | undefined;
|
|
7
|
-
askVolume: number | string | undefined;
|
|
8
|
-
timestamp?: number;
|
|
9
|
-
}
|
|
10
|
-
interface CcxtMarketLike {
|
|
11
|
-
symbol: string;
|
|
12
|
-
base: string;
|
|
13
|
-
quote: string;
|
|
14
|
-
type: string;
|
|
15
|
-
precision: {
|
|
16
|
-
price: number;
|
|
17
|
-
amount: number;
|
|
18
|
-
};
|
|
19
|
-
active: boolean;
|
|
20
|
-
}
|
|
21
|
-
interface CcxtOrderLike {
|
|
22
|
-
id?: string;
|
|
23
|
-
clientOrderId?: string;
|
|
24
|
-
symbol?: string;
|
|
25
|
-
side?: string;
|
|
26
|
-
type?: string;
|
|
27
|
-
status?: string;
|
|
28
|
-
amount?: number | string;
|
|
29
|
-
filled?: number | string;
|
|
30
|
-
price?: number | string;
|
|
31
|
-
timestamp?: number;
|
|
32
|
-
}
|
|
33
|
-
interface CcxtBalanceLike {
|
|
34
|
-
free?: Record<string, number | string | undefined>;
|
|
35
|
-
used?: Record<string, number | string | undefined>;
|
|
36
|
-
total?: Record<string, number | string | undefined>;
|
|
37
|
-
}
|
|
38
|
-
interface CcxtExchangeLike {
|
|
39
|
-
watchTicker(symbol: string): Promise<CcxtTickerLike>;
|
|
40
|
-
fetchMarkets(): Promise<CcxtMarketLike[]>;
|
|
41
|
-
fetchBalance(params?: Record<string, unknown>): Promise<CcxtBalanceLike>;
|
|
42
|
-
fetchOpenOrders(symbol?: string, since?: number, limit?: number, params?: Record<string, unknown>): Promise<CcxtOrderLike[]>;
|
|
43
|
-
createOrder(symbol: string, type: string, side: "buy" | "sell", amount: number, price?: number, params?: Record<string, unknown>): Promise<CcxtOrderLike>;
|
|
44
|
-
cancelOrder(id?: string, symbol?: string, params?: Record<string, unknown>): Promise<CcxtOrderLike>;
|
|
45
|
-
}
|
|
46
|
-
export interface CreateCcxtAsterAdapterInput {
|
|
47
|
-
exchange: CcxtExchangeLike;
|
|
48
|
-
}
|
|
49
|
-
export declare class CcxtAsterAdapter {
|
|
50
|
-
#private;
|
|
51
|
-
readonly exchange = "aster";
|
|
52
|
-
readonly capabilities: ExchangeCapabilities;
|
|
53
|
-
constructor(input: CreateCcxtAsterAdapterInput);
|
|
54
|
-
start(): Promise<void>;
|
|
55
|
-
stop(): Promise<void>;
|
|
56
|
-
subscribeL1Book(input: {
|
|
57
|
-
exchange: string;
|
|
58
|
-
symbol: string;
|
|
59
|
-
}): Promise<void>;
|
|
60
|
-
subscribeFundingRate(): Promise<void>;
|
|
61
|
-
watchMarketEvents(): import("../../runtime/async-queue.js").AsyncQueue<NormalizedMarketEvent>;
|
|
62
|
-
setMarketEventSink(sink: ((event: NormalizedMarketEvent) => void) | undefined): void;
|
|
63
|
-
watchInternalErrors(): import("../../runtime/async-queue.js").AsyncQueue<Error>;
|
|
64
|
-
subscribeOrders(): Promise<void>;
|
|
65
|
-
watchOrderEvents(): import("../../runtime/async-queue.js").AsyncQueue<NormalizedOrderEvent>;
|
|
66
|
-
setOrderEventSink(sink: ((event: NormalizedOrderEvent) => void) | undefined): void;
|
|
67
|
-
watchAccountEvents(): import("../../runtime/async-queue.js").AsyncQueue<NormalizedAccountEvent>;
|
|
68
|
-
setAccountEventSink(sink: ((event: NormalizedAccountEvent) => void) | undefined): void;
|
|
69
|
-
mapTickerToL1(symbol: string): Promise<{
|
|
70
|
-
receivedAt: number;
|
|
71
|
-
exchangeTs?: number;
|
|
72
|
-
exchange: string;
|
|
73
|
-
symbol: string;
|
|
74
|
-
bidPrice: string;
|
|
75
|
-
bidSize: string;
|
|
76
|
-
askPrice: string;
|
|
77
|
-
askSize: string;
|
|
78
|
-
}>;
|
|
79
|
-
fetchMarketInfo(): Promise<{
|
|
80
|
-
exchange: string;
|
|
81
|
-
symbol: string;
|
|
82
|
-
baseAsset: string;
|
|
83
|
-
quoteAsset: string;
|
|
84
|
-
marketType: string;
|
|
85
|
-
pricePrecision: number;
|
|
86
|
-
amountPrecision: number;
|
|
87
|
-
active: boolean;
|
|
88
|
-
}[]>;
|
|
89
|
-
fetchAccountBaseline(accountId: string): Promise<AdapterAccountBaseline>;
|
|
90
|
-
fetchOpenOrdersBaseline(accountId: string): Promise<{
|
|
91
|
-
clientOrderId?: string;
|
|
92
|
-
orderId?: string;
|
|
93
|
-
seq: number;
|
|
94
|
-
receivedAt: number;
|
|
95
|
-
updatedAt: number;
|
|
96
|
-
price?: string;
|
|
97
|
-
accountId: string;
|
|
98
|
-
exchange: string;
|
|
99
|
-
symbol: string;
|
|
100
|
-
side: "buy" | "sell";
|
|
101
|
-
type: string;
|
|
102
|
-
status: string;
|
|
103
|
-
amount: string;
|
|
104
|
-
filled: string;
|
|
105
|
-
}[]>;
|
|
106
|
-
placeOrder(input: {
|
|
107
|
-
accountId: string;
|
|
108
|
-
exchange: string;
|
|
109
|
-
symbol: string;
|
|
110
|
-
side: "buy" | "sell";
|
|
111
|
-
amount: string;
|
|
112
|
-
clientOrderId: string;
|
|
113
|
-
type: string;
|
|
114
|
-
price?: string;
|
|
115
|
-
reduceOnly?: boolean;
|
|
116
|
-
}): Promise<{
|
|
117
|
-
receivedAt: number;
|
|
118
|
-
orderId?: string;
|
|
119
|
-
clientOrderId: string;
|
|
120
|
-
}>;
|
|
121
|
-
amendOrder(input: {
|
|
122
|
-
accountId: string;
|
|
123
|
-
exchange: string;
|
|
124
|
-
clientOrderId?: string;
|
|
125
|
-
orderId?: string;
|
|
126
|
-
symbol?: string;
|
|
127
|
-
newPrice?: string;
|
|
128
|
-
}): Promise<{
|
|
129
|
-
receivedAt: number;
|
|
130
|
-
orderId?: string;
|
|
131
|
-
clientOrderId?: string;
|
|
132
|
-
}>;
|
|
133
|
-
cancelOrder(input: {
|
|
134
|
-
accountId: string;
|
|
135
|
-
exchange: string;
|
|
136
|
-
clientOrderId?: string;
|
|
137
|
-
orderId?: string;
|
|
138
|
-
}): Promise<{
|
|
139
|
-
receivedAt: number;
|
|
140
|
-
orderId?: string;
|
|
141
|
-
clientOrderId?: string;
|
|
142
|
-
}>;
|
|
143
|
-
cancelAllOrders(input: {
|
|
144
|
-
accountId: string;
|
|
145
|
-
exchange: string;
|
|
146
|
-
}): Promise<{
|
|
147
|
-
accountId: string;
|
|
148
|
-
exchange: string;
|
|
149
|
-
canceledCount: number;
|
|
150
|
-
}>;
|
|
151
|
-
getHealth(): {
|
|
152
|
-
exchange: string;
|
|
153
|
-
status: string;
|
|
154
|
-
wsConnected: boolean;
|
|
155
|
-
};
|
|
156
|
-
}
|
|
157
|
-
export {};
|
|
@@ -1,272 +0,0 @@
|
|
|
1
|
-
import { createAsyncQueue } from "../../runtime/async-queue.js";
|
|
2
|
-
export class CcxtAsterAdapter {
|
|
3
|
-
exchange = "aster";
|
|
4
|
-
capabilities = {
|
|
5
|
-
publicWs: true,
|
|
6
|
-
privateWs: true,
|
|
7
|
-
l1BookStream: true,
|
|
8
|
-
fundingRateStream: false,
|
|
9
|
-
accountStream: true,
|
|
10
|
-
orderStream: true,
|
|
11
|
-
fetchMarketInfo: true,
|
|
12
|
-
fetchBalances: true,
|
|
13
|
-
fetchPositions: false,
|
|
14
|
-
fetchRisk: false,
|
|
15
|
-
fetchOpenOrders: true,
|
|
16
|
-
fetchMyTrades: false,
|
|
17
|
-
fetchOrderById: false,
|
|
18
|
-
};
|
|
19
|
-
#exchange;
|
|
20
|
-
#started = false;
|
|
21
|
-
#marketQueue = createAsyncQueue({ maxBufferSize: 100 });
|
|
22
|
-
#marketEventSink;
|
|
23
|
-
#internalErrorQueue = createAsyncQueue({ maxBufferSize: 20 });
|
|
24
|
-
#orderQueue = createAsyncQueue({ maxBufferSize: 100 });
|
|
25
|
-
#orderEventSink;
|
|
26
|
-
#accountQueue = createAsyncQueue({ maxBufferSize: 100 });
|
|
27
|
-
#accountEventSink;
|
|
28
|
-
constructor(input) {
|
|
29
|
-
this.#exchange = input.exchange;
|
|
30
|
-
}
|
|
31
|
-
async start() {
|
|
32
|
-
this.#started = true;
|
|
33
|
-
}
|
|
34
|
-
async stop() {
|
|
35
|
-
this.#started = false;
|
|
36
|
-
this.#marketQueue.close();
|
|
37
|
-
this.#internalErrorQueue.close();
|
|
38
|
-
this.#orderQueue.close();
|
|
39
|
-
this.#accountQueue.close();
|
|
40
|
-
}
|
|
41
|
-
async subscribeL1Book(input) {
|
|
42
|
-
try {
|
|
43
|
-
const snapshot = await this.mapTickerToL1(input.symbol);
|
|
44
|
-
this.#emitMarketEvent({
|
|
45
|
-
type: "l1_book.updated",
|
|
46
|
-
exchange: snapshot.exchange,
|
|
47
|
-
symbol: snapshot.symbol,
|
|
48
|
-
bidPrice: snapshot.bidPrice,
|
|
49
|
-
bidSize: snapshot.bidSize,
|
|
50
|
-
askPrice: snapshot.askPrice,
|
|
51
|
-
askSize: snapshot.askSize,
|
|
52
|
-
...(snapshot.exchangeTs === undefined ? {} : { exchangeTs: snapshot.exchangeTs }),
|
|
53
|
-
receivedAt: snapshot.receivedAt,
|
|
54
|
-
});
|
|
55
|
-
}
|
|
56
|
-
catch (error) {
|
|
57
|
-
this.#emitInternalError(asError(error));
|
|
58
|
-
throw error;
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
async subscribeFundingRate() { }
|
|
62
|
-
watchMarketEvents() {
|
|
63
|
-
return this.#marketQueue;
|
|
64
|
-
}
|
|
65
|
-
setMarketEventSink(sink) {
|
|
66
|
-
this.#marketEventSink = sink;
|
|
67
|
-
}
|
|
68
|
-
watchInternalErrors() {
|
|
69
|
-
return this.#internalErrorQueue;
|
|
70
|
-
}
|
|
71
|
-
async subscribeOrders() { }
|
|
72
|
-
watchOrderEvents() {
|
|
73
|
-
return this.#orderQueue;
|
|
74
|
-
}
|
|
75
|
-
setOrderEventSink(sink) {
|
|
76
|
-
this.#orderEventSink = sink;
|
|
77
|
-
}
|
|
78
|
-
watchAccountEvents() {
|
|
79
|
-
return this.#accountQueue;
|
|
80
|
-
}
|
|
81
|
-
setAccountEventSink(sink) {
|
|
82
|
-
this.#accountEventSink = sink;
|
|
83
|
-
}
|
|
84
|
-
async mapTickerToL1(symbol) {
|
|
85
|
-
const ticker = await this.#exchange.watchTicker(symbol);
|
|
86
|
-
if (ticker.bid === undefined ||
|
|
87
|
-
ticker.bidVolume === undefined ||
|
|
88
|
-
ticker.ask === undefined ||
|
|
89
|
-
ticker.askVolume === undefined) {
|
|
90
|
-
throw new Error("missing required ticker fields");
|
|
91
|
-
}
|
|
92
|
-
return {
|
|
93
|
-
exchange: this.exchange,
|
|
94
|
-
symbol: ticker.symbol,
|
|
95
|
-
bidPrice: String(ticker.bid),
|
|
96
|
-
bidSize: String(ticker.bidVolume),
|
|
97
|
-
askPrice: String(ticker.ask),
|
|
98
|
-
askSize: String(ticker.askVolume),
|
|
99
|
-
...(ticker.timestamp === undefined ? {} : { exchangeTs: ticker.timestamp }),
|
|
100
|
-
receivedAt: Date.now(),
|
|
101
|
-
};
|
|
102
|
-
}
|
|
103
|
-
async fetchMarketInfo() {
|
|
104
|
-
const markets = await this.#exchange.fetchMarkets();
|
|
105
|
-
return markets.map((market) => {
|
|
106
|
-
return {
|
|
107
|
-
exchange: this.exchange,
|
|
108
|
-
symbol: market.symbol,
|
|
109
|
-
baseAsset: market.base,
|
|
110
|
-
quoteAsset: market.quote,
|
|
111
|
-
marketType: market.type,
|
|
112
|
-
pricePrecision: market.precision.price,
|
|
113
|
-
amountPrecision: market.precision.amount,
|
|
114
|
-
active: market.active,
|
|
115
|
-
};
|
|
116
|
-
});
|
|
117
|
-
}
|
|
118
|
-
async fetchAccountBaseline(accountId) {
|
|
119
|
-
const balance = await this.#exchange.fetchBalance();
|
|
120
|
-
const assets = new Set([
|
|
121
|
-
...Object.keys(balance.free ?? {}),
|
|
122
|
-
...Object.keys(balance.used ?? {}),
|
|
123
|
-
...Object.keys(balance.total ?? {}),
|
|
124
|
-
]);
|
|
125
|
-
const now = Date.now();
|
|
126
|
-
const balances = Object.fromEntries([...assets].map((asset) => {
|
|
127
|
-
return [
|
|
128
|
-
asset,
|
|
129
|
-
{
|
|
130
|
-
accountId,
|
|
131
|
-
exchange: this.exchange,
|
|
132
|
-
asset,
|
|
133
|
-
free: valueToString(balance.free?.[asset]),
|
|
134
|
-
used: valueToString(balance.used?.[asset]),
|
|
135
|
-
total: valueToString(balance.total?.[asset]),
|
|
136
|
-
seq: 0,
|
|
137
|
-
receivedAt: now,
|
|
138
|
-
updatedAt: now,
|
|
139
|
-
},
|
|
140
|
-
];
|
|
141
|
-
}));
|
|
142
|
-
return {
|
|
143
|
-
balances,
|
|
144
|
-
positions: [],
|
|
145
|
-
};
|
|
146
|
-
}
|
|
147
|
-
async fetchOpenOrdersBaseline(accountId) {
|
|
148
|
-
const orders = await this.#exchange.fetchOpenOrders();
|
|
149
|
-
const now = Date.now();
|
|
150
|
-
return orders.map((order) => {
|
|
151
|
-
const symbol = requiredString(order.symbol, "symbol");
|
|
152
|
-
const side = requiredOrderSide(order.side);
|
|
153
|
-
const type = requiredString(order.type, "type");
|
|
154
|
-
const status = requiredString(order.status, "status");
|
|
155
|
-
const amount = valueToRequiredString(order.amount, "amount");
|
|
156
|
-
const filled = valueToRequiredString(order.filled, "filled");
|
|
157
|
-
const updatedAt = order.timestamp ?? now;
|
|
158
|
-
return {
|
|
159
|
-
accountId,
|
|
160
|
-
exchange: this.exchange,
|
|
161
|
-
symbol,
|
|
162
|
-
side,
|
|
163
|
-
type,
|
|
164
|
-
status,
|
|
165
|
-
amount,
|
|
166
|
-
filled,
|
|
167
|
-
...(order.price === undefined ? {} : { price: String(order.price) }),
|
|
168
|
-
seq: 0,
|
|
169
|
-
receivedAt: now,
|
|
170
|
-
updatedAt,
|
|
171
|
-
...(order.id === undefined ? {} : { orderId: order.id }),
|
|
172
|
-
...(order.clientOrderId === undefined ? {} : { clientOrderId: order.clientOrderId }),
|
|
173
|
-
};
|
|
174
|
-
});
|
|
175
|
-
}
|
|
176
|
-
async placeOrder(input) {
|
|
177
|
-
const amount = parseFiniteNumber(input.amount, "amount");
|
|
178
|
-
const price = input.price === undefined ? undefined : parseFiniteNumber(input.price, "price");
|
|
179
|
-
const order = await this.#exchange.createOrder(input.symbol, input.type, input.side, amount, price, {
|
|
180
|
-
clientOrderId: input.clientOrderId,
|
|
181
|
-
});
|
|
182
|
-
return {
|
|
183
|
-
...(order.clientOrderId === undefined
|
|
184
|
-
? { clientOrderId: input.clientOrderId }
|
|
185
|
-
: { clientOrderId: order.clientOrderId }),
|
|
186
|
-
...(order.id === undefined ? {} : { orderId: order.id }),
|
|
187
|
-
receivedAt: Date.now(),
|
|
188
|
-
};
|
|
189
|
-
}
|
|
190
|
-
async amendOrder(input) {
|
|
191
|
-
return {
|
|
192
|
-
...(input.clientOrderId === undefined ? {} : { clientOrderId: input.clientOrderId }),
|
|
193
|
-
...(input.orderId === undefined ? {} : { orderId: input.orderId }),
|
|
194
|
-
receivedAt: Date.now(),
|
|
195
|
-
};
|
|
196
|
-
}
|
|
197
|
-
async cancelOrder(input) {
|
|
198
|
-
if (input.orderId === undefined && input.clientOrderId === undefined) {
|
|
199
|
-
return { receivedAt: Date.now() };
|
|
200
|
-
}
|
|
201
|
-
const order = await this.#exchange.cancelOrder(input.orderId, undefined, {
|
|
202
|
-
...(input.clientOrderId === undefined ? {} : { clientOrderId: input.clientOrderId }),
|
|
203
|
-
});
|
|
204
|
-
return {
|
|
205
|
-
...(order.clientOrderId === undefined
|
|
206
|
-
? input.clientOrderId === undefined
|
|
207
|
-
? {}
|
|
208
|
-
: { clientOrderId: input.clientOrderId }
|
|
209
|
-
: { clientOrderId: order.clientOrderId }),
|
|
210
|
-
...(order.id === undefined ? {} : { orderId: order.id }),
|
|
211
|
-
receivedAt: Date.now(),
|
|
212
|
-
};
|
|
213
|
-
}
|
|
214
|
-
async cancelAllOrders(input) {
|
|
215
|
-
return {
|
|
216
|
-
accountId: input.accountId,
|
|
217
|
-
exchange: input.exchange,
|
|
218
|
-
canceledCount: 0,
|
|
219
|
-
};
|
|
220
|
-
}
|
|
221
|
-
getHealth() {
|
|
222
|
-
return {
|
|
223
|
-
exchange: this.exchange,
|
|
224
|
-
status: this.#started ? "healthy" : "idle",
|
|
225
|
-
wsConnected: this.#started,
|
|
226
|
-
};
|
|
227
|
-
}
|
|
228
|
-
#emitMarketEvent(event) {
|
|
229
|
-
if (this.#marketEventSink !== undefined) {
|
|
230
|
-
this.#marketEventSink(event);
|
|
231
|
-
return;
|
|
232
|
-
}
|
|
233
|
-
this.#marketQueue.push(event);
|
|
234
|
-
}
|
|
235
|
-
#emitInternalError(error) {
|
|
236
|
-
this.#internalErrorQueue.push(error);
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
function asError(error) {
|
|
240
|
-
return error instanceof Error ? error : new Error(String(error));
|
|
241
|
-
}
|
|
242
|
-
function requiredString(value, field) {
|
|
243
|
-
if (value === undefined || value.trim().length === 0) {
|
|
244
|
-
throw new Error(`missing required order fields: ${field}`);
|
|
245
|
-
}
|
|
246
|
-
return value;
|
|
247
|
-
}
|
|
248
|
-
function requiredOrderSide(value) {
|
|
249
|
-
if (value !== "buy" && value !== "sell") {
|
|
250
|
-
throw new Error("missing required order fields: side");
|
|
251
|
-
}
|
|
252
|
-
return value;
|
|
253
|
-
}
|
|
254
|
-
function valueToRequiredString(value, field) {
|
|
255
|
-
if (value === undefined) {
|
|
256
|
-
throw new Error(`missing required order fields: ${field}`);
|
|
257
|
-
}
|
|
258
|
-
return String(value);
|
|
259
|
-
}
|
|
260
|
-
function valueToString(value) {
|
|
261
|
-
return value === undefined ? "0" : String(value);
|
|
262
|
-
}
|
|
263
|
-
function parseFiniteNumber(value, field) {
|
|
264
|
-
if (value.trim().length === 0) {
|
|
265
|
-
throw new Error(`invalid order ${field}`);
|
|
266
|
-
}
|
|
267
|
-
const parsed = Number(value);
|
|
268
|
-
if (!Number.isFinite(parsed)) {
|
|
269
|
-
throw new Error(`invalid order ${field}`);
|
|
270
|
-
}
|
|
271
|
-
return parsed;
|
|
272
|
-
}
|