@imbingox/acex 0.1.0-beta.1 → 0.1.0-beta.3

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.
@@ -1,12 +1,20 @@
1
+ import BigNumber from "bignumber.js";
2
+ import type { RawOrderUpdate } from "../adapters/types.ts";
1
3
  import type {
2
4
  AccountAwareManager,
3
5
  ClientContext,
4
6
  HealthReporter,
5
7
  ManagerLifecycle,
8
+ PrivateOrderDataConsumer,
9
+ PrivateSubscriptionState,
6
10
  } from "../client/context.ts";
11
+ import { AcexError } from "../errors.ts";
7
12
  import { AsyncEventBus } from "../internal/async-event-bus.ts";
8
13
  import { matchesOrderFilter } from "../internal/filters.ts";
9
14
  import type {
15
+ CancelAllOrdersInput,
16
+ CancelOrderInput,
17
+ CreateOrderInput,
10
18
  Exchange,
11
19
  GetOrderInput,
12
20
  OrderDataStatus,
@@ -32,12 +40,28 @@ function cloneOrderStatus(status: OrderDataStatus): OrderDataStatus {
32
40
  return { ...status };
33
41
  }
34
42
 
43
+ function getOrderLookupKey(input: {
44
+ orderId?: string;
45
+ clientOrderId?: string;
46
+ }): string | undefined {
47
+ if (input.orderId) {
48
+ return `order:${input.orderId}`;
49
+ }
50
+
51
+ if (input.clientOrderId) {
52
+ return `client:${input.clientOrderId}`;
53
+ }
54
+
55
+ return undefined;
56
+ }
57
+
35
58
  export class OrderManagerImpl
36
59
  implements
37
60
  OrderManager,
38
61
  ManagerLifecycle,
39
62
  AccountAwareManager,
40
- HealthReporter<OrderDataStatus>
63
+ HealthReporter<OrderDataStatus>,
64
+ PrivateOrderDataConsumer
41
65
  {
42
66
  readonly events: OrderEventStreams;
43
67
 
@@ -81,24 +105,13 @@ export class OrderManagerImpl
81
105
 
82
106
  const record = this.getOrCreateRecord(input.accountId, account.exchange);
83
107
  record.subscribed = true;
84
- record.status = {
85
- ...this.createStatus(input.accountId, account.exchange, "active"),
86
- ready: true,
87
- runtimeStatus: "healthy",
88
- lastReceivedAt: this.context.now(),
89
- lastReadyAt: this.context.now(),
90
- };
91
108
 
92
- const event: OrderSnapshotReplacedEvent = {
93
- type: "order.snapshot_replaced",
94
- accountId: record.accountId,
95
- exchange: record.exchange,
96
- snapshot: [...record.snapshots.values()],
97
- ts: this.context.now(),
98
- };
99
-
100
- this.orderBus.publish(event);
101
- this.publishStatus(record);
109
+ try {
110
+ await this.context.subscribePrivateOrderFeed(input.accountId);
111
+ } catch (error) {
112
+ record.subscribed = false;
113
+ throw error;
114
+ }
102
115
  }
103
116
 
104
117
  async unsubscribeOrders(input: UnsubscribeOrdersInput): Promise<void> {
@@ -107,16 +120,90 @@ export class OrderManagerImpl
107
120
  return;
108
121
  }
109
122
 
123
+ this.context.unsubscribePrivateOrderFeed(input.accountId);
110
124
  record.subscribed = false;
111
125
  record.status = {
112
126
  ...record.status,
113
127
  activity: "inactive",
114
128
  runtimeStatus: "stopped",
129
+ reason: undefined,
115
130
  inactiveSince: this.context.now(),
116
131
  };
117
132
  this.publishStatus(record);
118
133
  }
119
134
 
135
+ async createOrder(input: CreateOrderInput): Promise<OrderSnapshot> {
136
+ this.context.assertStarted();
137
+ const account = this.context.getRegisteredAccount(input.accountId);
138
+ this.context.ensurePrivateCredentials(input.accountId);
139
+ this.validateCreateOrderInput(input, account.exchange);
140
+
141
+ try {
142
+ const update = await this.context.createOrder(input);
143
+ return this.applyCommandUpdate(input.accountId, account.exchange, update);
144
+ } catch (error) {
145
+ throw this.wrapCommandError(
146
+ "ORDER_CREATE_FAILED",
147
+ `Failed to create order for ${input.accountId}: ${input.symbol}`,
148
+ error,
149
+ {
150
+ accountId: input.accountId,
151
+ exchange: account.exchange,
152
+ symbol: input.symbol,
153
+ },
154
+ );
155
+ }
156
+ }
157
+
158
+ async cancelOrder(input: CancelOrderInput): Promise<OrderSnapshot> {
159
+ this.context.assertStarted();
160
+ const account = this.context.getRegisteredAccount(input.accountId);
161
+ this.context.ensurePrivateCredentials(input.accountId);
162
+ this.validateCancelOrderInput(input, account.exchange);
163
+
164
+ try {
165
+ const update = await this.context.cancelOrder(input);
166
+ return this.applyCommandUpdate(input.accountId, account.exchange, update);
167
+ } catch (error) {
168
+ throw this.wrapCommandError(
169
+ "ORDER_CANCEL_FAILED",
170
+ `Failed to cancel order for ${input.accountId}: ${input.symbol}`,
171
+ error,
172
+ {
173
+ accountId: input.accountId,
174
+ exchange: account.exchange,
175
+ symbol: input.symbol,
176
+ },
177
+ );
178
+ }
179
+ }
180
+
181
+ async cancelAllOrders(input: CancelAllOrdersInput): Promise<OrderSnapshot[]> {
182
+ this.context.assertStarted();
183
+ const account = this.context.getRegisteredAccount(input.accountId);
184
+ this.context.ensurePrivateCredentials(input.accountId);
185
+
186
+ try {
187
+ const updates = await this.context.cancelAllOrders(input);
188
+ return this.applyCommandUpdates(
189
+ input.accountId,
190
+ account.exchange,
191
+ updates,
192
+ );
193
+ } catch (error) {
194
+ throw this.wrapCommandError(
195
+ "ORDER_CANCEL_ALL_FAILED",
196
+ `Failed to cancel all orders for ${input.accountId}: ${input.symbol}`,
197
+ error,
198
+ {
199
+ accountId: input.accountId,
200
+ exchange: account.exchange,
201
+ symbol: input.symbol,
202
+ },
203
+ );
204
+ }
205
+ }
206
+
120
207
  getOrder(input: GetOrderInput): OrderSnapshot | undefined {
121
208
  const record = this.records.get(input.accountId);
122
209
  if (!record) {
@@ -167,30 +254,7 @@ export class OrderManagerImpl
167
254
 
168
255
  // --- ManagerLifecycle ---
169
256
 
170
- onClientStarted(): void {
171
- const now = this.context.now();
172
-
173
- for (const [accountId, record] of this.records) {
174
- if (!record.subscribed) {
175
- continue;
176
- }
177
-
178
- const account = this.context.getRegisteredAccount(accountId);
179
- const creds = account.credentials;
180
- if (!creds?.apiKey || !creds.secret) {
181
- continue;
182
- }
183
-
184
- record.status = {
185
- ...this.createStatus(accountId, account.exchange, "active"),
186
- ready: true,
187
- runtimeStatus: "healthy",
188
- lastReceivedAt: now,
189
- lastReadyAt: now,
190
- };
191
- this.publishStatus(record);
192
- }
193
- }
257
+ onClientStarted(): void {}
194
258
 
195
259
  onClientStopping(now: number): void {
196
260
  for (const record of this.records.values()) {
@@ -202,6 +266,7 @@ export class OrderManagerImpl
202
266
  ...record.status,
203
267
  activity: "inactive",
204
268
  runtimeStatus: "stopped",
269
+ reason: undefined,
205
270
  inactiveSince: now,
206
271
  };
207
272
  this.publishStatus(record);
@@ -221,6 +286,7 @@ export class OrderManagerImpl
221
286
  ...record.status,
222
287
  activity: "inactive",
223
288
  runtimeStatus: "stopped",
289
+ reason: undefined,
224
290
  inactiveSince: now,
225
291
  };
226
292
  this.publishStatus(record);
@@ -233,10 +299,144 @@ export class OrderManagerImpl
233
299
  return;
234
300
  }
235
301
 
236
- record.status = this.createStatus(accountId, exchange, "active");
237
- record.status.ready = true;
238
- record.status.runtimeStatus = "healthy";
239
- record.status.lastReadyAt = this.context.now();
302
+ this.onPrivateOrderPending(accountId, exchange);
303
+ }
304
+
305
+ // --- PrivateOrderDataConsumer ---
306
+
307
+ onPrivateOrderPending(accountId: string, exchange: Exchange): void {
308
+ const record = this.getOrCreateRecord(accountId, exchange);
309
+ if (!record.subscribed) {
310
+ return;
311
+ }
312
+
313
+ record.status = {
314
+ ...this.createStatus(accountId, exchange, "active"),
315
+ ready: record.snapshots.size > 0,
316
+ runtimeStatus: "bootstrap_pending",
317
+ reason: undefined,
318
+ lastReceivedAt: record.status.lastReceivedAt,
319
+ lastReadyAt: record.status.lastReadyAt,
320
+ inactiveSince: undefined,
321
+ };
322
+ this.publishStatus(record);
323
+ }
324
+
325
+ onPrivateOrderBootstrap(
326
+ accountId: string,
327
+ exchange: Exchange,
328
+ snapshots: RawOrderUpdate[],
329
+ ): void {
330
+ const record = this.getOrCreateRecord(accountId, exchange);
331
+ if (!record.subscribed) {
332
+ return;
333
+ }
334
+
335
+ const nextSnapshots = new Map<string, OrderSnapshot>();
336
+ for (const update of snapshots) {
337
+ const snapshot = this.createSnapshot(
338
+ accountId,
339
+ exchange,
340
+ update,
341
+ this.getExistingSnapshot(record, update),
342
+ );
343
+ this.setSnapshot(nextSnapshots, snapshot);
344
+ }
345
+
346
+ record.snapshots = nextSnapshots;
347
+ const orderedSnapshots = [...record.snapshots.values()];
348
+ const latestTs = orderedSnapshots.reduce(
349
+ (max, snapshot) => Math.max(max, snapshot.updatedAt),
350
+ 0,
351
+ );
352
+ record.status = {
353
+ ...record.status,
354
+ activity: "active",
355
+ ready: true,
356
+ runtimeStatus: "healthy",
357
+ reason: undefined,
358
+ lastReceivedAt: latestTs || record.status.lastReceivedAt,
359
+ lastReadyAt: latestTs || this.context.now(),
360
+ inactiveSince: undefined,
361
+ };
362
+
363
+ const event: OrderSnapshotReplacedEvent = {
364
+ type: "order.snapshot_replaced",
365
+ accountId,
366
+ exchange,
367
+ snapshot: orderedSnapshots,
368
+ ts: this.context.now(),
369
+ };
370
+
371
+ this.orderBus.publish(event);
372
+ this.publishStatus(record);
373
+ }
374
+
375
+ onPrivateOrderUpdate(
376
+ accountId: string,
377
+ exchange: Exchange,
378
+ update: RawOrderUpdate,
379
+ ): void {
380
+ const record = this.getOrCreateRecord(accountId, exchange);
381
+ if (!record.subscribed) {
382
+ return;
383
+ }
384
+
385
+ const previous = this.getExistingSnapshot(record, update);
386
+ const snapshot = this.createSnapshot(accountId, exchange, update, previous);
387
+ this.setSnapshot(record.snapshots, snapshot);
388
+
389
+ const eventType =
390
+ snapshot.status === "filled"
391
+ ? "order.filled"
392
+ : snapshot.status === "rejected"
393
+ ? "order.rejected"
394
+ : snapshot.status === "canceled" || snapshot.status === "expired"
395
+ ? "order.canceled"
396
+ : "order.updated";
397
+
398
+ this.orderBus.publish({
399
+ type: eventType,
400
+ accountId,
401
+ exchange,
402
+ symbol: snapshot.symbol,
403
+ snapshot,
404
+ ts: this.context.now(),
405
+ });
406
+
407
+ record.status = {
408
+ ...record.status,
409
+ activity: "active",
410
+ ready: true,
411
+ runtimeStatus: "healthy",
412
+ reason: undefined,
413
+ lastReceivedAt: snapshot.receivedAt,
414
+ lastReadyAt: snapshot.updatedAt,
415
+ inactiveSince: undefined,
416
+ };
417
+ this.publishStatus(record);
418
+ }
419
+
420
+ onPrivateOrderStreamState(
421
+ accountId: string,
422
+ exchange: Exchange,
423
+ state: PrivateSubscriptionState,
424
+ ): void {
425
+ const record = this.getOrCreateRecord(accountId, exchange);
426
+ if (!record.subscribed) {
427
+ return;
428
+ }
429
+
430
+ record.status = {
431
+ ...record.status,
432
+ activity: "active",
433
+ ready: state.ready,
434
+ runtimeStatus: state.runtimeStatus,
435
+ reason: state.reason,
436
+ lastReceivedAt: state.lastReceivedAt ?? record.status.lastReceivedAt,
437
+ lastReadyAt: state.lastReadyAt ?? record.status.lastReadyAt,
438
+ inactiveSince: undefined,
439
+ };
240
440
  this.publishStatus(record);
241
441
  }
242
442
 
@@ -289,6 +489,86 @@ export class OrderManagerImpl
289
489
  };
290
490
  }
291
491
 
492
+ private getExistingSnapshot(
493
+ record: OrderRecord,
494
+ update: { orderId?: string; clientOrderId?: string },
495
+ ): OrderSnapshot | undefined {
496
+ for (const snapshot of record.snapshots.values()) {
497
+ if (update.orderId && snapshot.orderId === update.orderId) {
498
+ return snapshot;
499
+ }
500
+
501
+ if (
502
+ update.clientOrderId &&
503
+ snapshot.clientOrderId === update.clientOrderId
504
+ ) {
505
+ return snapshot;
506
+ }
507
+ }
508
+
509
+ return undefined;
510
+ }
511
+
512
+ private setSnapshot(
513
+ snapshots: Map<string, OrderSnapshot>,
514
+ snapshot: OrderSnapshot,
515
+ ): void {
516
+ const lookupKey =
517
+ getOrderLookupKey(snapshot) ??
518
+ getOrderLookupKey({
519
+ clientOrderId: snapshot.clientOrderId,
520
+ });
521
+ if (lookupKey) {
522
+ snapshots.set(lookupKey, snapshot);
523
+ }
524
+ }
525
+
526
+ private createSnapshot(
527
+ accountId: string,
528
+ exchange: Exchange,
529
+ input: RawOrderUpdate,
530
+ previous?: OrderSnapshot,
531
+ ): OrderSnapshot {
532
+ const amount = new BigNumber(input.amount);
533
+ const filled = new BigNumber(input.filled);
534
+ const remaining =
535
+ input.remaining === undefined
536
+ ? amount.minus(filled)
537
+ : new BigNumber(input.remaining);
538
+
539
+ return {
540
+ accountId,
541
+ exchange,
542
+ orderId: input.orderId,
543
+ clientOrderId: input.clientOrderId,
544
+ symbol: input.symbol,
545
+ side: input.side,
546
+ type: input.type,
547
+ status: input.status,
548
+ price:
549
+ input.price === undefined
550
+ ? previous?.price
551
+ : new BigNumber(input.price),
552
+ triggerPrice:
553
+ input.triggerPrice === undefined
554
+ ? previous?.triggerPrice
555
+ : new BigNumber(input.triggerPrice),
556
+ amount,
557
+ filled,
558
+ remaining,
559
+ reduceOnly: input.reduceOnly ?? previous?.reduceOnly,
560
+ positionSide: input.positionSide ?? previous?.positionSide,
561
+ avgFillPrice:
562
+ input.avgFillPrice === undefined
563
+ ? previous?.avgFillPrice
564
+ : new BigNumber(input.avgFillPrice),
565
+ exchangeTs: input.exchangeTs,
566
+ receivedAt: input.receivedAt,
567
+ updatedAt: input.receivedAt,
568
+ seq: (previous?.seq ?? 0) + 1,
569
+ };
570
+ }
571
+
292
572
  private publishStatus(record: OrderRecord): void {
293
573
  const event: OrderStatusChangedEvent = {
294
574
  type: "order.status_changed",
@@ -301,4 +581,105 @@ export class OrderManagerImpl
301
581
  this.orderStatusBus.publish(event);
302
582
  this.context.publishHealthEvent(event);
303
583
  }
584
+
585
+ private validateCreateOrderInput(
586
+ input: CreateOrderInput,
587
+ exchange: Exchange,
588
+ ): void {
589
+ if (input.type === "limit" && !input.price) {
590
+ throw this.createError(
591
+ "ORDER_INPUT_INVALID",
592
+ `Limit orders require price: ${input.accountId}`,
593
+ {
594
+ accountId: input.accountId,
595
+ exchange,
596
+ symbol: input.symbol,
597
+ },
598
+ );
599
+ }
600
+ }
601
+
602
+ private validateCancelOrderInput(
603
+ input: CancelOrderInput,
604
+ exchange: Exchange,
605
+ ): void {
606
+ if (input.orderId || input.clientOrderId) {
607
+ return;
608
+ }
609
+
610
+ throw this.createError(
611
+ "ORDER_INPUT_INVALID",
612
+ `cancelOrder requires orderId or clientOrderId: ${input.accountId}`,
613
+ {
614
+ accountId: input.accountId,
615
+ exchange,
616
+ symbol: input.symbol,
617
+ },
618
+ );
619
+ }
620
+
621
+ private applyCommandUpdate(
622
+ accountId: string,
623
+ exchange: Exchange,
624
+ update: RawOrderUpdate,
625
+ ): OrderSnapshot {
626
+ const record = this.getOrCreateRecord(accountId, exchange);
627
+ const previous = this.getExistingSnapshot(record, update);
628
+ const snapshot = this.createSnapshot(accountId, exchange, update, previous);
629
+ this.setSnapshot(record.snapshots, snapshot);
630
+ return snapshot;
631
+ }
632
+
633
+ private applyCommandUpdates(
634
+ accountId: string,
635
+ exchange: Exchange,
636
+ updates: RawOrderUpdate[],
637
+ ): OrderSnapshot[] {
638
+ return updates.map((update) =>
639
+ this.applyCommandUpdate(accountId, exchange, update),
640
+ );
641
+ }
642
+
643
+ private createError(
644
+ code:
645
+ | "ORDER_CANCEL_ALL_FAILED"
646
+ | "ORDER_CANCEL_FAILED"
647
+ | "ORDER_CREATE_FAILED"
648
+ | "ORDER_INPUT_INVALID",
649
+ message: string,
650
+ metadata: {
651
+ accountId: string;
652
+ exchange: Exchange;
653
+ symbol?: string;
654
+ },
655
+ ): AcexError {
656
+ const error = new AcexError(code, message);
657
+ this.context.publishRuntimeError("order", error, metadata);
658
+ return error;
659
+ }
660
+
661
+ private wrapCommandError(
662
+ code:
663
+ | "ORDER_CANCEL_ALL_FAILED"
664
+ | "ORDER_CANCEL_FAILED"
665
+ | "ORDER_CREATE_FAILED",
666
+ message: string,
667
+ error: unknown,
668
+ metadata: {
669
+ accountId: string;
670
+ exchange: Exchange;
671
+ symbol: string;
672
+ },
673
+ ): AcexError {
674
+ if (error instanceof AcexError) {
675
+ return error;
676
+ }
677
+
678
+ this.context.publishRuntimeError(
679
+ "adapter",
680
+ error instanceof Error ? error : new Error(message),
681
+ metadata,
682
+ );
683
+ return new AcexError(code, message);
684
+ }
304
685
  }
@@ -1,5 +1,7 @@
1
+ import type BigNumber from "bignumber.js";
1
2
  import type {
2
3
  Exchange,
4
+ PrivateRuntimeReason,
3
5
  PrivateRuntimeStatus,
4
6
  SubscriptionActivity,
5
7
  } from "./shared.ts";
@@ -13,12 +15,7 @@ export interface AccountDataStatus {
13
15
  lastReceivedAt?: number;
14
16
  lastReadyAt?: number;
15
17
  inactiveSince?: number;
16
- reason?:
17
- | "credentials_missing"
18
- | "auth_failed"
19
- | "ws_disconnected"
20
- | "heartbeat_timeout"
21
- | "reconciling";
18
+ reason?: PrivateRuntimeReason;
22
19
  }
23
20
 
24
21
  export interface AccountStatusChangedEvent {
@@ -55,9 +52,9 @@ export interface BalanceSnapshot {
55
52
  accountId: string;
56
53
  exchange: Exchange;
57
54
  asset: string;
58
- free: string;
59
- used: string;
60
- total: string;
55
+ free: BigNumber;
56
+ used: BigNumber;
57
+ total: BigNumber;
61
58
  exchangeTs?: number;
62
59
  receivedAt: number;
63
60
  updatedAt: number;
@@ -69,12 +66,12 @@ export interface PositionSnapshot {
69
66
  exchange: Exchange;
70
67
  symbol: string;
71
68
  side: PositionSide;
72
- size: string;
73
- entryPrice?: string;
74
- markPrice?: string;
75
- unrealizedPnl?: string;
76
- leverage?: string;
77
- liquidationPrice?: string;
69
+ size: BigNumber;
70
+ entryPrice?: BigNumber;
71
+ markPrice?: BigNumber;
72
+ unrealizedPnl?: BigNumber;
73
+ leverage?: BigNumber;
74
+ liquidationPrice?: BigNumber;
78
75
  exchangeTs?: number;
79
76
  receivedAt: number;
80
77
  updatedAt: number;
@@ -84,10 +81,10 @@ export interface PositionSnapshot {
84
81
  export interface RiskSnapshot {
85
82
  accountId: string;
86
83
  exchange: Exchange;
87
- equity?: string;
88
- marginRatio?: string;
89
- initialMargin?: string;
90
- maintenanceMargin?: string;
84
+ equity?: BigNumber;
85
+ marginRatio?: BigNumber;
86
+ initialMargin?: BigNumber;
87
+ maintenanceMargin?: BigNumber;
91
88
  exchangeTs?: number;
92
89
  receivedAt: number;
93
90
  updatedAt: number;
@@ -1,3 +1,4 @@
1
+ import type BigNumber from "bignumber.js";
1
2
  import type {
2
3
  Exchange,
3
4
  MarketFreshness,
@@ -18,13 +19,13 @@ export interface MarketDefinition {
18
19
  contract: boolean;
19
20
  linear?: boolean;
20
21
  inverse?: boolean;
21
- contractSize?: string;
22
+ contractSize?: BigNumber;
22
23
  pricePrecision: number;
23
24
  amountPrecision: number;
24
- priceStep: string;
25
- amountStep: string;
26
- minAmount?: string;
27
- minNotional?: string;
25
+ priceStep: BigNumber;
26
+ amountStep: BigNumber;
27
+ minAmount?: BigNumber;
28
+ minNotional?: BigNumber;
28
29
  expiry?: number;
29
30
  raw: Record<string, unknown>;
30
31
  }
@@ -58,10 +59,10 @@ export interface MarketEventFilter {
58
59
  export interface L1Book {
59
60
  exchange: Exchange;
60
61
  symbol: string;
61
- bidPrice: string;
62
- bidSize: string;
63
- askPrice: string;
64
- askSize: string;
62
+ bidPrice: BigNumber;
63
+ bidSize: BigNumber;
64
+ askPrice: BigNumber;
65
+ askSize: BigNumber;
65
66
  exchangeTs?: number;
66
67
  receivedAt: number;
67
68
  updatedAt: number;
@@ -71,10 +72,10 @@ export interface L1Book {
71
72
  export interface FundingRateSnapshot {
72
73
  exchange: Exchange;
73
74
  symbol: string;
74
- fundingRate: string;
75
+ fundingRate: BigNumber;
75
76
  nextFundingTime?: number;
76
- markPrice?: string;
77
- indexPrice?: string;
77
+ markPrice?: BigNumber;
78
+ indexPrice?: BigNumber;
78
79
  exchangeTs?: number;
79
80
  receivedAt: number;
80
81
  updatedAt: number;
@@ -128,8 +129,9 @@ export interface MarketManager {
128
129
  subscribeFundingRate(input: SubscribeFundingRateInput): Promise<void>;
129
130
  unsubscribeFundingRate(input: SubscribeFundingRateInput): Promise<void>;
130
131
 
131
- getMarket(symbol: string): MarketDefinition | undefined;
132
- listMarkets(): MarketDefinition[];
132
+ getMarket(exchange: Exchange, symbol: string): MarketDefinition | undefined;
133
+ findMarkets(symbol: string): MarketDefinition[];
134
+ listMarkets(exchange?: Exchange): MarketDefinition[];
133
135
  getL1Book(key: MarketKeyInput): L1Book | undefined;
134
136
  getFundingRate(key: MarketKeyInput): FundingRateSnapshot | undefined;
135
137
  getMarketStatus(key: MarketKeyInput): MarketDataStatus | undefined;