@imbingox/acex 0.3.0-beta.1 → 0.3.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.
- package/README.md +71 -24
- package/docs/api.md +1457 -0
- package/package.json +5 -2
- package/src/adapters/binance/adapter.ts +11 -2
- package/src/adapters/binance/market-catalog.ts +2 -2
- package/src/adapters/binance/private-adapter.ts +44 -4
- package/src/adapters/juplend/private-adapter.ts +517 -0
- package/src/adapters/types.ts +34 -4
- package/src/client/context.ts +16 -11
- package/src/client/private-subscription-coordinator.ts +101 -47
- package/src/client/runtime.ts +64 -20
- package/src/client/venue-capabilities.ts +109 -0
- package/src/errors.ts +1 -1
- package/src/internal/filters.ts +9 -9
- package/src/managers/account-manager.ts +95 -58
- package/src/managers/market-manager.ts +45 -45
- package/src/managers/order-manager.ts +49 -56
- package/src/types/account.ts +30 -10
- package/src/types/client.ts +73 -2
- package/src/types/market.ts +12 -16
- package/src/types/order.ts +7 -7
- package/src/types/shared.ts +43 -7
|
@@ -18,7 +18,6 @@ import { AcexError } from "../errors.ts";
|
|
|
18
18
|
import { AsyncEventBus } from "../internal/async-event-bus.ts";
|
|
19
19
|
import { matchesMarketFilter } from "../internal/filters.ts";
|
|
20
20
|
import type {
|
|
21
|
-
Exchange,
|
|
22
21
|
FundingRateSnapshot,
|
|
23
22
|
FundingRateUpdatedEvent,
|
|
24
23
|
L1Book,
|
|
@@ -36,6 +35,7 @@ import type {
|
|
|
36
35
|
SubscribeFundingRateInput,
|
|
37
36
|
SubscribeL1BookInput,
|
|
38
37
|
SubscriptionActivity,
|
|
38
|
+
Venue,
|
|
39
39
|
} from "../types/index.ts";
|
|
40
40
|
|
|
41
41
|
export interface MarketManagerOptions {
|
|
@@ -46,7 +46,7 @@ export interface MarketManagerOptions {
|
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
interface MarketRecord {
|
|
49
|
-
|
|
49
|
+
venue: Venue;
|
|
50
50
|
symbol: string;
|
|
51
51
|
market?: MarketDefinition;
|
|
52
52
|
l1Book?: L1Book;
|
|
@@ -68,7 +68,7 @@ const DEFAULT_L1_RECONNECT_DELAY_MS = 1_000;
|
|
|
68
68
|
const DEFAULT_L1_RECONNECT_MAX_DELAY_MS = 10_000;
|
|
69
69
|
|
|
70
70
|
function marketKey(input: MarketKeyInput): string {
|
|
71
|
-
return `${input.
|
|
71
|
+
return `${input.venue}:${input.symbol}`;
|
|
72
72
|
}
|
|
73
73
|
|
|
74
74
|
function cloneMarketStatus(status: MarketDataStatus): MarketDataStatus {
|
|
@@ -169,7 +169,7 @@ export class MarketManagerImpl
|
|
|
169
169
|
this.context.assertStarted();
|
|
170
170
|
const market = await this.resolveMarketDefinition(input);
|
|
171
171
|
const record = this.getOrCreateRecord({
|
|
172
|
-
|
|
172
|
+
venue: input.venue,
|
|
173
173
|
symbol: market.symbol,
|
|
174
174
|
});
|
|
175
175
|
|
|
@@ -202,7 +202,7 @@ export class MarketManagerImpl
|
|
|
202
202
|
const market = await this.resolveMarketDefinition(input);
|
|
203
203
|
this.assertFundingRateSupported(market);
|
|
204
204
|
const record = this.getOrCreateRecord({
|
|
205
|
-
|
|
205
|
+
venue: input.venue,
|
|
206
206
|
symbol: market.symbol,
|
|
207
207
|
});
|
|
208
208
|
|
|
@@ -232,22 +232,22 @@ export class MarketManagerImpl
|
|
|
232
232
|
this.recomputeAndPublishStatus(record, this.context.now());
|
|
233
233
|
}
|
|
234
234
|
|
|
235
|
-
getMarket(
|
|
236
|
-
const market = this.definitions.get(marketKey({
|
|
235
|
+
getMarket(venue: Venue, symbol: string): MarketDefinition | undefined {
|
|
236
|
+
const market = this.definitions.get(marketKey({ venue, symbol }));
|
|
237
237
|
return market ? cloneMarketDefinition(market) : undefined;
|
|
238
238
|
}
|
|
239
239
|
|
|
240
240
|
getMarkets(symbol: string): MarketDefinition[] {
|
|
241
241
|
return [...this.definitions.values()]
|
|
242
242
|
.filter((market) => market.symbol === symbol)
|
|
243
|
-
.sort((left, right) => left.
|
|
243
|
+
.sort((left, right) => left.venue.localeCompare(right.venue))
|
|
244
244
|
.map((market) => cloneMarketDefinition(market));
|
|
245
245
|
}
|
|
246
246
|
|
|
247
|
-
listMarkets(
|
|
247
|
+
listMarkets(venue?: Venue): MarketDefinition[] {
|
|
248
248
|
const values = [...this.definitions.values()];
|
|
249
|
-
const filtered =
|
|
250
|
-
? values.filter((market) => market.
|
|
249
|
+
const filtered = venue
|
|
250
|
+
? values.filter((market) => market.venue === venue)
|
|
251
251
|
: values;
|
|
252
252
|
return filtered
|
|
253
253
|
.sort((left, right) => left.symbol.localeCompare(right.symbol))
|
|
@@ -324,7 +324,7 @@ export class MarketManagerImpl
|
|
|
324
324
|
record.symbol === symbol && Boolean(record.l1Book),
|
|
325
325
|
)
|
|
326
326
|
.map((record) => cloneL1Book(record.l1Book))
|
|
327
|
-
.sort((left, right) => left.
|
|
327
|
+
.sort((left, right) => left.venue.localeCompare(right.venue));
|
|
328
328
|
}
|
|
329
329
|
|
|
330
330
|
getFundingRate(key: MarketKeyInput): FundingRateSnapshot | undefined {
|
|
@@ -341,7 +341,7 @@ export class MarketManagerImpl
|
|
|
341
341
|
record.symbol === symbol && Boolean(record.fundingRate),
|
|
342
342
|
)
|
|
343
343
|
.map((record) => cloneFundingRate(record.fundingRate))
|
|
344
|
-
.sort((left, right) => left.
|
|
344
|
+
.sort((left, right) => left.venue.localeCompare(right.venue));
|
|
345
345
|
}
|
|
346
346
|
|
|
347
347
|
getMarketStatus(key: MarketKeyInput): MarketDataStatus | undefined {
|
|
@@ -406,8 +406,8 @@ export class MarketManagerImpl
|
|
|
406
406
|
return [...this.records.values()]
|
|
407
407
|
.map((record) => cloneMarketStatus(record.status))
|
|
408
408
|
.sort((left, right) =>
|
|
409
|
-
`${left.
|
|
410
|
-
`${right.
|
|
409
|
+
`${left.venue}:${left.symbol}`.localeCompare(
|
|
410
|
+
`${right.venue}:${right.symbol}`,
|
|
411
411
|
),
|
|
412
412
|
);
|
|
413
413
|
}
|
|
@@ -450,17 +450,17 @@ export class MarketManagerImpl
|
|
|
450
450
|
error instanceof Error
|
|
451
451
|
? error
|
|
452
452
|
: new Error("Unknown catalog load failure"),
|
|
453
|
-
{
|
|
453
|
+
{ venue: this.adapter.venue },
|
|
454
454
|
);
|
|
455
455
|
throw wrapped;
|
|
456
456
|
}
|
|
457
457
|
}
|
|
458
458
|
|
|
459
459
|
private async resolveMarketDefinition(input: {
|
|
460
|
-
|
|
460
|
+
venue: Venue;
|
|
461
461
|
symbol: string;
|
|
462
462
|
}): Promise<MarketDefinition> {
|
|
463
|
-
this.
|
|
463
|
+
this.assertSupportedVenue(input.venue);
|
|
464
464
|
await this.loadMarketCatalog();
|
|
465
465
|
|
|
466
466
|
const market = this.definitions.get(marketKey(input));
|
|
@@ -468,7 +468,7 @@ export class MarketManagerImpl
|
|
|
468
468
|
throw this.createError(
|
|
469
469
|
"MARKET_NOT_FOUND",
|
|
470
470
|
`Unknown market symbol: ${input.symbol}`,
|
|
471
|
-
{
|
|
471
|
+
{ venue: input.venue, symbol: input.symbol },
|
|
472
472
|
"market",
|
|
473
473
|
);
|
|
474
474
|
}
|
|
@@ -477,7 +477,7 @@ export class MarketManagerImpl
|
|
|
477
477
|
throw this.createError(
|
|
478
478
|
"MARKET_INACTIVE",
|
|
479
479
|
`Inactive market symbol: ${input.symbol}`,
|
|
480
|
-
{
|
|
480
|
+
{ venue: input.venue, symbol: input.symbol },
|
|
481
481
|
"market",
|
|
482
482
|
);
|
|
483
483
|
}
|
|
@@ -491,7 +491,7 @@ export class MarketManagerImpl
|
|
|
491
491
|
throw this.createError(
|
|
492
492
|
"MARKET_NOT_FOUND",
|
|
493
493
|
`Unknown market symbol: ${input.symbol}`,
|
|
494
|
-
{
|
|
494
|
+
{ venue: input.venue, symbol: input.symbol },
|
|
495
495
|
"market",
|
|
496
496
|
);
|
|
497
497
|
}
|
|
@@ -499,15 +499,15 @@ export class MarketManagerImpl
|
|
|
499
499
|
return market;
|
|
500
500
|
}
|
|
501
501
|
|
|
502
|
-
private
|
|
503
|
-
if (
|
|
502
|
+
private assertSupportedVenue(venue: Venue): void {
|
|
503
|
+
if (venue === this.adapter.venue) {
|
|
504
504
|
return;
|
|
505
505
|
}
|
|
506
506
|
|
|
507
507
|
throw this.createError(
|
|
508
|
-
"
|
|
509
|
-
`
|
|
510
|
-
{
|
|
508
|
+
"VENUE_NOT_SUPPORTED",
|
|
509
|
+
`Venue is not supported yet: ${venue}`,
|
|
510
|
+
{ venue },
|
|
511
511
|
"client",
|
|
512
512
|
);
|
|
513
513
|
}
|
|
@@ -520,13 +520,13 @@ export class MarketManagerImpl
|
|
|
520
520
|
throw this.createError(
|
|
521
521
|
"MARKET_FUNDING_RATE_UNSUPPORTED",
|
|
522
522
|
`Funding rate is not supported for market: ${market.symbol}`,
|
|
523
|
-
{
|
|
523
|
+
{ venue: market.venue, symbol: market.symbol },
|
|
524
524
|
"market",
|
|
525
525
|
);
|
|
526
526
|
}
|
|
527
527
|
|
|
528
528
|
private getOrCreateRecord(input: {
|
|
529
|
-
|
|
529
|
+
venue: Venue;
|
|
530
530
|
symbol: string;
|
|
531
531
|
}): MarketRecord {
|
|
532
532
|
const key = marketKey(input);
|
|
@@ -536,12 +536,12 @@ export class MarketManagerImpl
|
|
|
536
536
|
}
|
|
537
537
|
|
|
538
538
|
const record: MarketRecord = {
|
|
539
|
-
|
|
539
|
+
venue: input.venue,
|
|
540
540
|
symbol: input.symbol,
|
|
541
541
|
l1BookSubscribed: false,
|
|
542
542
|
fundingRateSubscribed: false,
|
|
543
543
|
status: {
|
|
544
|
-
|
|
544
|
+
venue: input.venue,
|
|
545
545
|
symbol: input.symbol,
|
|
546
546
|
activity: "inactive",
|
|
547
547
|
ready: false,
|
|
@@ -572,7 +572,7 @@ export class MarketManagerImpl
|
|
|
572
572
|
`Timed out waiting for market data: ${market.symbol}`,
|
|
573
573
|
);
|
|
574
574
|
this.context.publishRuntimeError("runtime", timeoutError, {
|
|
575
|
-
|
|
575
|
+
venue: market.venue,
|
|
576
576
|
symbol: market.symbol,
|
|
577
577
|
});
|
|
578
578
|
this.updateConnectionState(record, "l1Book", "stale", "ws_disconnected");
|
|
@@ -600,7 +600,7 @@ export class MarketManagerImpl
|
|
|
600
600
|
`Timed out waiting for market data: ${market.symbol}`,
|
|
601
601
|
);
|
|
602
602
|
this.context.publishRuntimeError("runtime", timeoutError, {
|
|
603
|
-
|
|
603
|
+
venue: market.venue,
|
|
604
604
|
symbol: market.symbol,
|
|
605
605
|
});
|
|
606
606
|
this.updateConnectionState(
|
|
@@ -620,7 +620,7 @@ export class MarketManagerImpl
|
|
|
620
620
|
const callbacks: L1BookStreamCallbacks = {
|
|
621
621
|
onUpdate: (update: RawL1BookUpdate) => {
|
|
622
622
|
record.l1Book = this.createL1Book(
|
|
623
|
-
record.
|
|
623
|
+
record.venue,
|
|
624
624
|
record.symbol,
|
|
625
625
|
update,
|
|
626
626
|
record.l1Book,
|
|
@@ -631,7 +631,7 @@ export class MarketManagerImpl
|
|
|
631
631
|
|
|
632
632
|
const event: L1BookUpdatedEvent = {
|
|
633
633
|
type: "l1_book.updated",
|
|
634
|
-
|
|
634
|
+
venue: record.venue,
|
|
635
635
|
symbol: record.symbol,
|
|
636
636
|
snapshot: cloneL1Book(record.l1Book),
|
|
637
637
|
ts: this.context.now(),
|
|
@@ -653,7 +653,7 @@ export class MarketManagerImpl
|
|
|
653
653
|
},
|
|
654
654
|
onError: (error) => {
|
|
655
655
|
this.context.publishRuntimeError("runtime", error, {
|
|
656
|
-
|
|
656
|
+
venue: record.venue,
|
|
657
657
|
symbol: record.symbol,
|
|
658
658
|
});
|
|
659
659
|
},
|
|
@@ -677,7 +677,7 @@ export class MarketManagerImpl
|
|
|
677
677
|
const callbacks: FundingRateStreamCallbacks = {
|
|
678
678
|
onUpdate: (update: RawFundingRateUpdate) => {
|
|
679
679
|
record.fundingRate = this.createFundingRate(
|
|
680
|
-
record.
|
|
680
|
+
record.venue,
|
|
681
681
|
record.symbol,
|
|
682
682
|
update,
|
|
683
683
|
record.fundingRate,
|
|
@@ -688,7 +688,7 @@ export class MarketManagerImpl
|
|
|
688
688
|
|
|
689
689
|
const event: FundingRateUpdatedEvent = {
|
|
690
690
|
type: "funding_rate.updated",
|
|
691
|
-
|
|
691
|
+
venue: record.venue,
|
|
692
692
|
symbol: record.symbol,
|
|
693
693
|
snapshot: cloneFundingRate(record.fundingRate),
|
|
694
694
|
ts: this.context.now(),
|
|
@@ -710,7 +710,7 @@ export class MarketManagerImpl
|
|
|
710
710
|
},
|
|
711
711
|
onError: (error) => {
|
|
712
712
|
this.context.publishRuntimeError("runtime", error, {
|
|
713
|
-
|
|
713
|
+
venue: record.venue,
|
|
714
714
|
symbol: record.symbol,
|
|
715
715
|
});
|
|
716
716
|
},
|
|
@@ -728,13 +728,13 @@ export class MarketManagerImpl
|
|
|
728
728
|
}
|
|
729
729
|
|
|
730
730
|
private createL1Book(
|
|
731
|
-
|
|
731
|
+
venue: Venue,
|
|
732
732
|
symbol: string,
|
|
733
733
|
input: RawL1BookUpdate,
|
|
734
734
|
previous?: L1Book,
|
|
735
735
|
): L1Book {
|
|
736
736
|
return {
|
|
737
|
-
|
|
737
|
+
venue,
|
|
738
738
|
symbol,
|
|
739
739
|
bidPrice: new BigNumber(input.bidPrice),
|
|
740
740
|
bidSize: new BigNumber(input.bidSize),
|
|
@@ -755,13 +755,13 @@ export class MarketManagerImpl
|
|
|
755
755
|
}
|
|
756
756
|
|
|
757
757
|
private createFundingRate(
|
|
758
|
-
|
|
758
|
+
venue: Venue,
|
|
759
759
|
symbol: string,
|
|
760
760
|
input: RawFundingRateUpdate,
|
|
761
761
|
previous?: FundingRateSnapshot,
|
|
762
762
|
): FundingRateSnapshot {
|
|
763
763
|
return {
|
|
764
|
-
|
|
764
|
+
venue,
|
|
765
765
|
symbol,
|
|
766
766
|
fundingRate: new BigNumber(input.fundingRate),
|
|
767
767
|
nextFundingTime: input.nextFundingTime,
|
|
@@ -936,7 +936,7 @@ export class MarketManagerImpl
|
|
|
936
936
|
private publishStatus(record: MarketRecord): void {
|
|
937
937
|
const event: MarketStatusChangedEvent = {
|
|
938
938
|
type: "market.status_changed",
|
|
939
|
-
|
|
939
|
+
venue: record.venue,
|
|
940
940
|
symbol: record.symbol,
|
|
941
941
|
status: cloneMarketStatus(record.status),
|
|
942
942
|
ts: this.context.now(),
|
|
@@ -985,9 +985,9 @@ export class MarketManagerImpl
|
|
|
985
985
|
| "MARKET_NOT_FOUND"
|
|
986
986
|
| "MARKET_INACTIVE"
|
|
987
987
|
| "MARKET_FUNDING_RATE_UNSUPPORTED"
|
|
988
|
-
| "
|
|
988
|
+
| "VENUE_NOT_SUPPORTED",
|
|
989
989
|
message: string,
|
|
990
|
-
metadata?: {
|
|
990
|
+
metadata?: { venue?: Venue; symbol?: string },
|
|
991
991
|
source: "market" | "client" = "market",
|
|
992
992
|
): AcexError {
|
|
993
993
|
const error = new AcexError(code, message);
|
|
@@ -15,7 +15,6 @@ import type {
|
|
|
15
15
|
CancelAllOrdersInput,
|
|
16
16
|
CancelOrderInput,
|
|
17
17
|
CreateOrderInput,
|
|
18
|
-
Exchange,
|
|
19
18
|
GetOrderInput,
|
|
20
19
|
OrderDataStatus,
|
|
21
20
|
OrderEvent,
|
|
@@ -26,11 +25,12 @@ import type {
|
|
|
26
25
|
OrderStatusChangedEvent,
|
|
27
26
|
SubscribeOrdersInput,
|
|
28
27
|
UnsubscribeOrdersInput,
|
|
28
|
+
Venue,
|
|
29
29
|
} from "../types/index.ts";
|
|
30
30
|
|
|
31
31
|
interface OrderRecord {
|
|
32
32
|
accountId: string;
|
|
33
|
-
|
|
33
|
+
venue: Venue;
|
|
34
34
|
subscribed: boolean;
|
|
35
35
|
snapshots: Map<string, OrderSnapshot>;
|
|
36
36
|
status: OrderDataStatus;
|
|
@@ -78,7 +78,7 @@ export class OrderManagerImpl
|
|
|
78
78
|
status: (filter) =>
|
|
79
79
|
this.orderStatusBus.stream((event) =>
|
|
80
80
|
matchesOrderFilter(
|
|
81
|
-
{ accountId: event.accountId,
|
|
81
|
+
{ accountId: event.accountId, venue: event.venue },
|
|
82
82
|
filter,
|
|
83
83
|
),
|
|
84
84
|
),
|
|
@@ -87,7 +87,7 @@ export class OrderManagerImpl
|
|
|
87
87
|
matchesOrderFilter(
|
|
88
88
|
{
|
|
89
89
|
accountId: event.accountId,
|
|
90
|
-
|
|
90
|
+
venue: event.venue,
|
|
91
91
|
symbol: "symbol" in event ? event.symbol : undefined,
|
|
92
92
|
},
|
|
93
93
|
filter,
|
|
@@ -103,7 +103,7 @@ export class OrderManagerImpl
|
|
|
103
103
|
const account = this.context.getRegisteredAccount(input.accountId);
|
|
104
104
|
this.context.ensurePrivateCredentials(input.accountId);
|
|
105
105
|
|
|
106
|
-
const record = this.getOrCreateRecord(input.accountId, account.
|
|
106
|
+
const record = this.getOrCreateRecord(input.accountId, account.venue);
|
|
107
107
|
record.subscribed = true;
|
|
108
108
|
|
|
109
109
|
try {
|
|
@@ -136,11 +136,11 @@ export class OrderManagerImpl
|
|
|
136
136
|
this.context.assertStarted();
|
|
137
137
|
const account = this.context.getRegisteredAccount(input.accountId);
|
|
138
138
|
this.context.ensurePrivateCredentials(input.accountId);
|
|
139
|
-
this.validateCreateOrderInput(input, account.
|
|
139
|
+
this.validateCreateOrderInput(input, account.venue);
|
|
140
140
|
|
|
141
141
|
try {
|
|
142
142
|
const update = await this.context.createOrder(input);
|
|
143
|
-
return this.applyCommandUpdate(input.accountId, account.
|
|
143
|
+
return this.applyCommandUpdate(input.accountId, account.venue, update);
|
|
144
144
|
} catch (error) {
|
|
145
145
|
throw this.wrapCommandError(
|
|
146
146
|
"ORDER_CREATE_FAILED",
|
|
@@ -148,7 +148,7 @@ export class OrderManagerImpl
|
|
|
148
148
|
error,
|
|
149
149
|
{
|
|
150
150
|
accountId: input.accountId,
|
|
151
|
-
|
|
151
|
+
venue: account.venue,
|
|
152
152
|
symbol: input.symbol,
|
|
153
153
|
},
|
|
154
154
|
);
|
|
@@ -159,11 +159,11 @@ export class OrderManagerImpl
|
|
|
159
159
|
this.context.assertStarted();
|
|
160
160
|
const account = this.context.getRegisteredAccount(input.accountId);
|
|
161
161
|
this.context.ensurePrivateCredentials(input.accountId);
|
|
162
|
-
this.validateCancelOrderInput(input, account.
|
|
162
|
+
this.validateCancelOrderInput(input, account.venue);
|
|
163
163
|
|
|
164
164
|
try {
|
|
165
165
|
const update = await this.context.cancelOrder(input);
|
|
166
|
-
return this.applyCommandUpdate(input.accountId, account.
|
|
166
|
+
return this.applyCommandUpdate(input.accountId, account.venue, update);
|
|
167
167
|
} catch (error) {
|
|
168
168
|
throw this.wrapCommandError(
|
|
169
169
|
"ORDER_CANCEL_FAILED",
|
|
@@ -171,7 +171,7 @@ export class OrderManagerImpl
|
|
|
171
171
|
error,
|
|
172
172
|
{
|
|
173
173
|
accountId: input.accountId,
|
|
174
|
-
|
|
174
|
+
venue: account.venue,
|
|
175
175
|
symbol: input.symbol,
|
|
176
176
|
},
|
|
177
177
|
);
|
|
@@ -185,11 +185,7 @@ export class OrderManagerImpl
|
|
|
185
185
|
|
|
186
186
|
try {
|
|
187
187
|
const updates = await this.context.cancelAllOrders(input);
|
|
188
|
-
return this.applyCommandUpdates(
|
|
189
|
-
input.accountId,
|
|
190
|
-
account.exchange,
|
|
191
|
-
updates,
|
|
192
|
-
);
|
|
188
|
+
return this.applyCommandUpdates(input.accountId, account.venue, updates);
|
|
193
189
|
} catch (error) {
|
|
194
190
|
throw this.wrapCommandError(
|
|
195
191
|
"ORDER_CANCEL_ALL_FAILED",
|
|
@@ -197,7 +193,7 @@ export class OrderManagerImpl
|
|
|
197
193
|
error,
|
|
198
194
|
{
|
|
199
195
|
accountId: input.accountId,
|
|
200
|
-
|
|
196
|
+
venue: account.venue,
|
|
201
197
|
symbol: input.symbol,
|
|
202
198
|
},
|
|
203
199
|
);
|
|
@@ -293,25 +289,25 @@ export class OrderManagerImpl
|
|
|
293
289
|
this.records.delete(accountId);
|
|
294
290
|
}
|
|
295
291
|
|
|
296
|
-
onCredentialsUpdated(accountId: string,
|
|
292
|
+
onCredentialsUpdated(accountId: string, venue: Venue): void {
|
|
297
293
|
const record = this.records.get(accountId);
|
|
298
294
|
if (!record?.subscribed) {
|
|
299
295
|
return;
|
|
300
296
|
}
|
|
301
297
|
|
|
302
|
-
this.onPrivateOrderPending(accountId,
|
|
298
|
+
this.onPrivateOrderPending(accountId, venue);
|
|
303
299
|
}
|
|
304
300
|
|
|
305
301
|
// --- PrivateOrderDataConsumer ---
|
|
306
302
|
|
|
307
|
-
onPrivateOrderPending(accountId: string,
|
|
308
|
-
const record = this.getOrCreateRecord(accountId,
|
|
303
|
+
onPrivateOrderPending(accountId: string, venue: Venue): void {
|
|
304
|
+
const record = this.getOrCreateRecord(accountId, venue);
|
|
309
305
|
if (!record.subscribed) {
|
|
310
306
|
return;
|
|
311
307
|
}
|
|
312
308
|
|
|
313
309
|
record.status = {
|
|
314
|
-
...this.createStatus(accountId,
|
|
310
|
+
...this.createStatus(accountId, venue, "active"),
|
|
315
311
|
ready: record.snapshots.size > 0,
|
|
316
312
|
runtimeStatus: "bootstrap_pending",
|
|
317
313
|
reason: undefined,
|
|
@@ -324,10 +320,10 @@ export class OrderManagerImpl
|
|
|
324
320
|
|
|
325
321
|
onPrivateOrderBootstrap(
|
|
326
322
|
accountId: string,
|
|
327
|
-
|
|
323
|
+
venue: Venue,
|
|
328
324
|
snapshots: RawOrderUpdate[],
|
|
329
325
|
): void {
|
|
330
|
-
const record = this.getOrCreateRecord(accountId,
|
|
326
|
+
const record = this.getOrCreateRecord(accountId, venue);
|
|
331
327
|
if (!record.subscribed) {
|
|
332
328
|
return;
|
|
333
329
|
}
|
|
@@ -336,7 +332,7 @@ export class OrderManagerImpl
|
|
|
336
332
|
for (const update of snapshots) {
|
|
337
333
|
const snapshot = this.createSnapshot(
|
|
338
334
|
accountId,
|
|
339
|
-
|
|
335
|
+
venue,
|
|
340
336
|
update,
|
|
341
337
|
this.getExistingSnapshot(record, update),
|
|
342
338
|
);
|
|
@@ -363,7 +359,7 @@ export class OrderManagerImpl
|
|
|
363
359
|
const event: OrderSnapshotReplacedEvent = {
|
|
364
360
|
type: "order.snapshot_replaced",
|
|
365
361
|
accountId,
|
|
366
|
-
|
|
362
|
+
venue,
|
|
367
363
|
snapshot: orderedSnapshots,
|
|
368
364
|
ts: this.context.now(),
|
|
369
365
|
};
|
|
@@ -374,16 +370,16 @@ export class OrderManagerImpl
|
|
|
374
370
|
|
|
375
371
|
onPrivateOrderUpdate(
|
|
376
372
|
accountId: string,
|
|
377
|
-
|
|
373
|
+
venue: Venue,
|
|
378
374
|
update: RawOrderUpdate,
|
|
379
375
|
): void {
|
|
380
|
-
const record = this.getOrCreateRecord(accountId,
|
|
376
|
+
const record = this.getOrCreateRecord(accountId, venue);
|
|
381
377
|
if (!record.subscribed) {
|
|
382
378
|
return;
|
|
383
379
|
}
|
|
384
380
|
|
|
385
381
|
const previous = this.getExistingSnapshot(record, update);
|
|
386
|
-
const snapshot = this.createSnapshot(accountId,
|
|
382
|
+
const snapshot = this.createSnapshot(accountId, venue, update, previous);
|
|
387
383
|
this.setSnapshot(record.snapshots, snapshot);
|
|
388
384
|
|
|
389
385
|
const eventType =
|
|
@@ -398,7 +394,7 @@ export class OrderManagerImpl
|
|
|
398
394
|
this.orderBus.publish({
|
|
399
395
|
type: eventType,
|
|
400
396
|
accountId,
|
|
401
|
-
|
|
397
|
+
venue,
|
|
402
398
|
symbol: snapshot.symbol,
|
|
403
399
|
snapshot,
|
|
404
400
|
ts: this.context.now(),
|
|
@@ -419,10 +415,10 @@ export class OrderManagerImpl
|
|
|
419
415
|
|
|
420
416
|
onPrivateOrderStreamState(
|
|
421
417
|
accountId: string,
|
|
422
|
-
|
|
418
|
+
venue: Venue,
|
|
423
419
|
state: PrivateSubscriptionState,
|
|
424
420
|
): void {
|
|
425
|
-
const record = this.getOrCreateRecord(accountId,
|
|
421
|
+
const record = this.getOrCreateRecord(accountId, venue);
|
|
426
422
|
if (!record.subscribed) {
|
|
427
423
|
return;
|
|
428
424
|
}
|
|
@@ -446,18 +442,15 @@ export class OrderManagerImpl
|
|
|
446
442
|
return [...this.records.values()]
|
|
447
443
|
.map((record) => cloneOrderStatus(record.status))
|
|
448
444
|
.sort((left, right) =>
|
|
449
|
-
`${left.
|
|
450
|
-
`${right.
|
|
445
|
+
`${left.venue}:${left.accountId}`.localeCompare(
|
|
446
|
+
`${right.venue}:${right.accountId}`,
|
|
451
447
|
),
|
|
452
448
|
);
|
|
453
449
|
}
|
|
454
450
|
|
|
455
451
|
// --- Internal helpers ---
|
|
456
452
|
|
|
457
|
-
private getOrCreateRecord(
|
|
458
|
-
accountId: string,
|
|
459
|
-
exchange: Exchange,
|
|
460
|
-
): OrderRecord {
|
|
453
|
+
private getOrCreateRecord(accountId: string, venue: Venue): OrderRecord {
|
|
461
454
|
const existing = this.records.get(accountId);
|
|
462
455
|
if (existing) {
|
|
463
456
|
return existing;
|
|
@@ -465,10 +458,10 @@ export class OrderManagerImpl
|
|
|
465
458
|
|
|
466
459
|
const record: OrderRecord = {
|
|
467
460
|
accountId,
|
|
468
|
-
|
|
461
|
+
venue,
|
|
469
462
|
subscribed: false,
|
|
470
463
|
snapshots: new Map(),
|
|
471
|
-
status: this.createStatus(accountId,
|
|
464
|
+
status: this.createStatus(accountId, venue, "inactive"),
|
|
472
465
|
};
|
|
473
466
|
|
|
474
467
|
this.records.set(accountId, record);
|
|
@@ -477,12 +470,12 @@ export class OrderManagerImpl
|
|
|
477
470
|
|
|
478
471
|
private createStatus(
|
|
479
472
|
accountId: string,
|
|
480
|
-
|
|
473
|
+
venue: Venue,
|
|
481
474
|
activity: "active" | "inactive",
|
|
482
475
|
): OrderDataStatus {
|
|
483
476
|
return {
|
|
484
477
|
accountId,
|
|
485
|
-
|
|
478
|
+
venue,
|
|
486
479
|
activity,
|
|
487
480
|
ready: false,
|
|
488
481
|
runtimeStatus: activity === "active" ? "bootstrap_pending" : "stopped",
|
|
@@ -525,7 +518,7 @@ export class OrderManagerImpl
|
|
|
525
518
|
|
|
526
519
|
private createSnapshot(
|
|
527
520
|
accountId: string,
|
|
528
|
-
|
|
521
|
+
venue: Venue,
|
|
529
522
|
input: RawOrderUpdate,
|
|
530
523
|
previous?: OrderSnapshot,
|
|
531
524
|
): OrderSnapshot {
|
|
@@ -538,7 +531,7 @@ export class OrderManagerImpl
|
|
|
538
531
|
|
|
539
532
|
return {
|
|
540
533
|
accountId,
|
|
541
|
-
|
|
534
|
+
venue,
|
|
542
535
|
orderId: input.orderId,
|
|
543
536
|
clientOrderId: input.clientOrderId,
|
|
544
537
|
symbol: input.symbol,
|
|
@@ -573,7 +566,7 @@ export class OrderManagerImpl
|
|
|
573
566
|
const event: OrderStatusChangedEvent = {
|
|
574
567
|
type: "order.status_changed",
|
|
575
568
|
accountId: record.accountId,
|
|
576
|
-
|
|
569
|
+
venue: record.venue,
|
|
577
570
|
status: cloneOrderStatus(record.status),
|
|
578
571
|
ts: this.context.now(),
|
|
579
572
|
};
|
|
@@ -584,7 +577,7 @@ export class OrderManagerImpl
|
|
|
584
577
|
|
|
585
578
|
private validateCreateOrderInput(
|
|
586
579
|
input: CreateOrderInput,
|
|
587
|
-
|
|
580
|
+
venue: Venue,
|
|
588
581
|
): void {
|
|
589
582
|
if (input.type === "limit" && !input.price) {
|
|
590
583
|
throw this.createError(
|
|
@@ -592,7 +585,7 @@ export class OrderManagerImpl
|
|
|
592
585
|
`Limit orders require price: ${input.accountId}`,
|
|
593
586
|
{
|
|
594
587
|
accountId: input.accountId,
|
|
595
|
-
|
|
588
|
+
venue,
|
|
596
589
|
symbol: input.symbol,
|
|
597
590
|
},
|
|
598
591
|
);
|
|
@@ -601,7 +594,7 @@ export class OrderManagerImpl
|
|
|
601
594
|
|
|
602
595
|
private validateCancelOrderInput(
|
|
603
596
|
input: CancelOrderInput,
|
|
604
|
-
|
|
597
|
+
venue: Venue,
|
|
605
598
|
): void {
|
|
606
599
|
if (input.orderId || input.clientOrderId) {
|
|
607
600
|
return;
|
|
@@ -612,7 +605,7 @@ export class OrderManagerImpl
|
|
|
612
605
|
`cancelOrder requires orderId or clientOrderId: ${input.accountId}`,
|
|
613
606
|
{
|
|
614
607
|
accountId: input.accountId,
|
|
615
|
-
|
|
608
|
+
venue,
|
|
616
609
|
symbol: input.symbol,
|
|
617
610
|
},
|
|
618
611
|
);
|
|
@@ -620,23 +613,23 @@ export class OrderManagerImpl
|
|
|
620
613
|
|
|
621
614
|
private applyCommandUpdate(
|
|
622
615
|
accountId: string,
|
|
623
|
-
|
|
616
|
+
venue: Venue,
|
|
624
617
|
update: RawOrderUpdate,
|
|
625
618
|
): OrderSnapshot {
|
|
626
|
-
const record = this.getOrCreateRecord(accountId,
|
|
619
|
+
const record = this.getOrCreateRecord(accountId, venue);
|
|
627
620
|
const previous = this.getExistingSnapshot(record, update);
|
|
628
|
-
const snapshot = this.createSnapshot(accountId,
|
|
621
|
+
const snapshot = this.createSnapshot(accountId, venue, update, previous);
|
|
629
622
|
this.setSnapshot(record.snapshots, snapshot);
|
|
630
623
|
return snapshot;
|
|
631
624
|
}
|
|
632
625
|
|
|
633
626
|
private applyCommandUpdates(
|
|
634
627
|
accountId: string,
|
|
635
|
-
|
|
628
|
+
venue: Venue,
|
|
636
629
|
updates: RawOrderUpdate[],
|
|
637
630
|
): OrderSnapshot[] {
|
|
638
631
|
return updates.map((update) =>
|
|
639
|
-
this.applyCommandUpdate(accountId,
|
|
632
|
+
this.applyCommandUpdate(accountId, venue, update),
|
|
640
633
|
);
|
|
641
634
|
}
|
|
642
635
|
|
|
@@ -649,7 +642,7 @@ export class OrderManagerImpl
|
|
|
649
642
|
message: string,
|
|
650
643
|
metadata: {
|
|
651
644
|
accountId: string;
|
|
652
|
-
|
|
645
|
+
venue: Venue;
|
|
653
646
|
symbol?: string;
|
|
654
647
|
},
|
|
655
648
|
): AcexError {
|
|
@@ -667,7 +660,7 @@ export class OrderManagerImpl
|
|
|
667
660
|
error: unknown,
|
|
668
661
|
metadata: {
|
|
669
662
|
accountId: string;
|
|
670
|
-
|
|
663
|
+
venue: Venue;
|
|
671
664
|
symbol: string;
|
|
672
665
|
},
|
|
673
666
|
): AcexError {
|