@imbingox/acex 0.4.0-beta.12 → 0.4.0-beta.13
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/docs/api.md +4 -2
- package/package.json +2 -1
- package/src/adapters/binance/private-adapter.ts +210 -37
- package/src/adapters/types.ts +2 -0
- package/src/client/private-subscription-coordinator.ts +31 -0
- package/src/internal/watermark.ts +11 -0
- package/src/managers/order/data-status.ts +61 -0
- package/src/managers/order/identity.ts +77 -0
- package/src/managers/order/model.ts +36 -0
- package/src/managers/order/snapshot.ts +87 -0
- package/src/managers/order/store.ts +486 -0
- package/src/managers/order-manager.ts +168 -720
- package/src/types/shared.ts +1 -0
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import BigNumber from "bignumber.js";
|
|
2
1
|
import type {
|
|
3
2
|
RawOpenOrdersSnapshot,
|
|
4
3
|
RawOrderUpdate,
|
|
@@ -17,7 +16,6 @@ import {
|
|
|
17
16
|
formatAcexErrorMessage,
|
|
18
17
|
} from "../errors.ts";
|
|
19
18
|
import { AsyncEventBus } from "../internal/async-event-bus.ts";
|
|
20
|
-
import { toCanonical } from "../internal/decimal.ts";
|
|
21
19
|
import { matchesOrderFilter } from "../internal/filters.ts";
|
|
22
20
|
import { isTransportError } from "../internal/http-client.ts";
|
|
23
21
|
import {
|
|
@@ -40,170 +38,41 @@ import type {
|
|
|
40
38
|
UnsubscribeOrdersInput,
|
|
41
39
|
Venue,
|
|
42
40
|
} from "../types/index.ts";
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
const SYSTEM_CLIENT_ORDER_ID_PATTERNS = [
|
|
80
|
-
/^adl_autoclose$/,
|
|
81
|
-
/^autoclose-/,
|
|
82
|
-
/^settlement_autoclose-/,
|
|
83
|
-
];
|
|
84
|
-
|
|
85
|
-
function cloneOrderStatus(status: OrderDataStatus): OrderDataStatus {
|
|
86
|
-
return { ...status };
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
function normalizeMaxClosedOrdersPerSymbol(value: number | undefined): number {
|
|
90
|
-
return value !== undefined && Number.isInteger(value) && value > 0
|
|
91
|
-
? value
|
|
92
|
-
: DEFAULT_MAX_CLOSED_ORDERS_PER_SYMBOL;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
function getOrderLookupKeys(input: {
|
|
96
|
-
symbol: string;
|
|
97
|
-
orderId?: string;
|
|
98
|
-
clientOrderId?: string;
|
|
99
|
-
}): string[] {
|
|
100
|
-
const keys: string[] = [];
|
|
101
|
-
if (input.orderId) {
|
|
102
|
-
keys.push(`symbol:${input.symbol}:order:${input.orderId}`);
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
if (input.clientOrderId) {
|
|
106
|
-
keys.push(`symbol:${input.symbol}:client:${input.clientOrderId}`);
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
return keys;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
function shouldMatchOrderQuery(
|
|
113
|
-
candidate: OrderSnapshot,
|
|
114
|
-
input: { symbol?: string; orderId?: string; clientOrderId?: string },
|
|
115
|
-
): boolean {
|
|
116
|
-
if (input.symbol && candidate.symbol !== input.symbol) {
|
|
117
|
-
return false;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
if (input.orderId && candidate.orderId !== input.orderId) {
|
|
121
|
-
return false;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
if (input.clientOrderId && candidate.clientOrderId !== input.clientOrderId) {
|
|
125
|
-
return false;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
return Boolean(input.orderId || input.clientOrderId);
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
function shouldMatchStoredOrderIdentity(
|
|
132
|
-
candidate: OrderSnapshot,
|
|
133
|
-
input: { symbol: string; orderId?: string; clientOrderId?: string },
|
|
134
|
-
): boolean {
|
|
135
|
-
if (candidate.symbol !== input.symbol) {
|
|
136
|
-
return false;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
if (candidate.orderId && input.orderId) {
|
|
140
|
-
return candidate.orderId === input.orderId;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
// clientOrderId is only a temporary identity for an order that does not yet
|
|
144
|
-
// have an orderId. A candidate that already carries an orderId (including an
|
|
145
|
-
// old order sitting in closed that reused this clientOrderId) must not be
|
|
146
|
-
// merged by a cid-only update; otherwise the stale orderId would be
|
|
147
|
-
// carried forward and pollute closed. When the orderId is later filled in,
|
|
148
|
-
// the candidate still lacks an orderId and matches normally.
|
|
149
|
-
return Boolean(
|
|
150
|
-
input.clientOrderId &&
|
|
151
|
-
candidate.clientOrderId === input.clientOrderId &&
|
|
152
|
-
!candidate.orderId,
|
|
153
|
-
);
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
function successfulStatus(
|
|
157
|
-
status: OrderDataStatus,
|
|
158
|
-
options: {
|
|
159
|
-
ready?: boolean;
|
|
160
|
-
lastReceivedAt?: number;
|
|
161
|
-
lastReadyAt?: number;
|
|
162
|
-
preserveStatus?: boolean;
|
|
163
|
-
},
|
|
164
|
-
): OrderDataStatus {
|
|
165
|
-
const preservesStreamState =
|
|
166
|
-
options.preserveStatus &&
|
|
167
|
-
(status.runtimeStatus === "reconnecting" ||
|
|
168
|
-
status.reason === "ws_disconnected" ||
|
|
169
|
-
status.reason === "heartbeat_timeout");
|
|
170
|
-
const ready = options.ready ?? true;
|
|
171
|
-
|
|
172
|
-
return {
|
|
173
|
-
...status,
|
|
174
|
-
activity: "active",
|
|
175
|
-
ready,
|
|
176
|
-
runtimeStatus: preservesStreamState ? status.runtimeStatus : "healthy",
|
|
177
|
-
reason: preservesStreamState ? status.reason : undefined,
|
|
178
|
-
lastReceivedAt: options.lastReceivedAt ?? status.lastReceivedAt,
|
|
179
|
-
lastReadyAt: ready
|
|
180
|
-
? (options.lastReadyAt ??
|
|
181
|
-
(options.preserveStatus ? status.lastReadyAt : undefined) ??
|
|
182
|
-
Date.now())
|
|
183
|
-
: status.lastReadyAt,
|
|
184
|
-
inactiveSince: undefined,
|
|
185
|
-
};
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
function isOpenOrder(snapshot: OrderSnapshot): boolean {
|
|
189
|
-
return snapshot.status === "open" || snapshot.status === "partially_filled";
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
function orderPriority(status: OrderSnapshot["status"]): number {
|
|
193
|
-
switch (status) {
|
|
194
|
-
case "filled":
|
|
195
|
-
return 5;
|
|
196
|
-
case "canceled":
|
|
197
|
-
case "expired":
|
|
198
|
-
return 4;
|
|
199
|
-
case "rejected":
|
|
200
|
-
return 3;
|
|
201
|
-
case "partially_filled":
|
|
202
|
-
return 2;
|
|
203
|
-
case "open":
|
|
204
|
-
return 1;
|
|
205
|
-
}
|
|
206
|
-
}
|
|
41
|
+
import {
|
|
42
|
+
cloneOrderStatus,
|
|
43
|
+
createOrderDataStatus,
|
|
44
|
+
normalizeMaxClosedOrdersPerSymbol,
|
|
45
|
+
successfulStatus,
|
|
46
|
+
} from "./order/data-status.ts";
|
|
47
|
+
import {
|
|
48
|
+
getOrderLookupKeys,
|
|
49
|
+
isSystemClientOrderId,
|
|
50
|
+
SDK_CLIENT_ORDER_ID_PREFIX,
|
|
51
|
+
shouldMatchOrderQuery,
|
|
52
|
+
VENUE_CLIENT_ORDER_ID_PATTERN,
|
|
53
|
+
} from "./order/identity.ts";
|
|
54
|
+
import type {
|
|
55
|
+
OrderLocation,
|
|
56
|
+
OrderManagerOptions,
|
|
57
|
+
OrderRecord,
|
|
58
|
+
} from "./order/model.ts";
|
|
59
|
+
import { createSnapshot, isOpenOrder } from "./order/snapshot.ts";
|
|
60
|
+
import {
|
|
61
|
+
getAllSnapshots,
|
|
62
|
+
getExistingSnapshot,
|
|
63
|
+
getLocalOrderIdForVenueOrderId,
|
|
64
|
+
getLocationByLocalOrderId,
|
|
65
|
+
getOpenOrderSnapshots,
|
|
66
|
+
getSnapshotAtLocation,
|
|
67
|
+
getSnapshotByLocalOrderId,
|
|
68
|
+
getSnapshotCount,
|
|
69
|
+
getSnapshotsForClientOrderId,
|
|
70
|
+
getSnapshotsForOrderId,
|
|
71
|
+
isVenueClientOrderIdInUseForOpenOrder,
|
|
72
|
+
resolveLocalOrderIdForUpdate,
|
|
73
|
+
selectLatestSnapshot,
|
|
74
|
+
setSnapshot,
|
|
75
|
+
} from "./order/store.ts";
|
|
207
76
|
|
|
208
77
|
export class OrderManagerImpl
|
|
209
78
|
implements
|
|
@@ -320,13 +189,25 @@ export class OrderManagerImpl
|
|
|
320
189
|
...input,
|
|
321
190
|
clientOrderId: venueClientOrderId,
|
|
322
191
|
};
|
|
192
|
+
const requestStartedAt = this.context.now();
|
|
323
193
|
const update = await this.context.createOrder(commandInput);
|
|
324
194
|
const snapshot = this.applyCommandUpdate(
|
|
325
195
|
input.accountId,
|
|
326
196
|
account.venue,
|
|
327
197
|
update,
|
|
328
|
-
{ localOrderId },
|
|
198
|
+
{ localOrderId, requestStartedAt },
|
|
329
199
|
);
|
|
200
|
+
if (!snapshot) {
|
|
201
|
+
throw this.createError(
|
|
202
|
+
"ORDER_CREATE_FAILED",
|
|
203
|
+
`Failed to store created order snapshot for ${input.accountId}: ${input.symbol}`,
|
|
204
|
+
{
|
|
205
|
+
accountId: input.accountId,
|
|
206
|
+
venue: account.venue,
|
|
207
|
+
symbol: input.symbol,
|
|
208
|
+
},
|
|
209
|
+
);
|
|
210
|
+
}
|
|
330
211
|
this.clearPendingClientOrderClaim(
|
|
331
212
|
record,
|
|
332
213
|
venueClientOrderId,
|
|
@@ -361,8 +242,29 @@ export class OrderManagerImpl
|
|
|
361
242
|
this.validateCancelOrderInput(input, account.venue);
|
|
362
243
|
|
|
363
244
|
try {
|
|
245
|
+
const requestStartedAt = this.context.now();
|
|
364
246
|
const update = await this.context.cancelOrder(input);
|
|
365
|
-
|
|
247
|
+
const snapshot = this.applyCommandUpdate(
|
|
248
|
+
input.accountId,
|
|
249
|
+
account.venue,
|
|
250
|
+
update,
|
|
251
|
+
{
|
|
252
|
+
requestStartedAt,
|
|
253
|
+
},
|
|
254
|
+
);
|
|
255
|
+
if (!snapshot) {
|
|
256
|
+
throw this.createError(
|
|
257
|
+
"ORDER_CANCEL_FAILED",
|
|
258
|
+
`Failed to store canceled order snapshot for ${input.accountId}: ${input.symbol}`,
|
|
259
|
+
{
|
|
260
|
+
accountId: input.accountId,
|
|
261
|
+
venue: account.venue,
|
|
262
|
+
symbol: input.symbol,
|
|
263
|
+
},
|
|
264
|
+
);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
return snapshot;
|
|
366
268
|
} catch (error) {
|
|
367
269
|
throw this.wrapCommandError(
|
|
368
270
|
"ORDER_CANCEL_FAILED",
|
|
@@ -383,8 +285,29 @@ export class OrderManagerImpl
|
|
|
383
285
|
this.context.ensurePrivateCredentials(input.accountId);
|
|
384
286
|
|
|
385
287
|
try {
|
|
288
|
+
const requestStartedAt = this.context.now();
|
|
386
289
|
const updates = await this.context.cancelAllOrders(input);
|
|
387
|
-
|
|
290
|
+
const snapshots = this.applyCommandUpdates(
|
|
291
|
+
input.accountId,
|
|
292
|
+
account.venue,
|
|
293
|
+
updates,
|
|
294
|
+
{
|
|
295
|
+
requestStartedAt,
|
|
296
|
+
},
|
|
297
|
+
);
|
|
298
|
+
if (!snapshots) {
|
|
299
|
+
throw this.createError(
|
|
300
|
+
"ORDER_CANCEL_ALL_FAILED",
|
|
301
|
+
`Failed to store canceled order snapshots for ${input.accountId}: ${input.symbol}`,
|
|
302
|
+
{
|
|
303
|
+
accountId: input.accountId,
|
|
304
|
+
venue: account.venue,
|
|
305
|
+
symbol: input.symbol,
|
|
306
|
+
},
|
|
307
|
+
);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
return snapshots;
|
|
388
311
|
} catch (error) {
|
|
389
312
|
throw this.wrapCommandError(
|
|
390
313
|
"ORDER_CANCEL_ALL_FAILED",
|
|
@@ -410,13 +333,13 @@ export class OrderManagerImpl
|
|
|
410
333
|
}
|
|
411
334
|
|
|
412
335
|
if (input.symbol && input.orderId) {
|
|
413
|
-
const localOrderId =
|
|
336
|
+
const localOrderId = getLocalOrderIdForVenueOrderId(
|
|
414
337
|
record,
|
|
415
338
|
input.symbol,
|
|
416
339
|
input.orderId,
|
|
417
340
|
);
|
|
418
341
|
const snapshot = localOrderId
|
|
419
|
-
?
|
|
342
|
+
? getSnapshotByLocalOrderId(record, localOrderId)
|
|
420
343
|
: undefined;
|
|
421
344
|
if (!snapshot) {
|
|
422
345
|
return undefined;
|
|
@@ -433,16 +356,16 @@ export class OrderManagerImpl
|
|
|
433
356
|
}
|
|
434
357
|
|
|
435
358
|
if (input.orderId) {
|
|
436
|
-
return
|
|
437
|
-
|
|
359
|
+
return selectLatestSnapshot(
|
|
360
|
+
getSnapshotsForOrderId(record, input.orderId).filter((snapshot) =>
|
|
438
361
|
shouldMatchOrderQuery(snapshot, input),
|
|
439
362
|
),
|
|
440
363
|
);
|
|
441
364
|
}
|
|
442
365
|
|
|
443
366
|
if (input.clientOrderId) {
|
|
444
|
-
return
|
|
445
|
-
|
|
367
|
+
return selectLatestSnapshot(
|
|
368
|
+
getSnapshotsForClientOrderId(record, input.clientOrderId).filter(
|
|
446
369
|
(snapshot) => shouldMatchOrderQuery(snapshot, input),
|
|
447
370
|
),
|
|
448
371
|
);
|
|
@@ -457,11 +380,7 @@ export class OrderManagerImpl
|
|
|
457
380
|
return [];
|
|
458
381
|
}
|
|
459
382
|
|
|
460
|
-
|
|
461
|
-
return [...(record.openOrders.get(symbol)?.values() ?? [])];
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
return this.getOpenOrderSnapshots(record);
|
|
383
|
+
return getOpenOrderSnapshots(record, symbol);
|
|
465
384
|
}
|
|
466
385
|
|
|
467
386
|
getOrderStatus(accountId: string): OrderDataStatus | undefined {
|
|
@@ -528,8 +447,8 @@ export class OrderManagerImpl
|
|
|
528
447
|
}
|
|
529
448
|
|
|
530
449
|
record.status = {
|
|
531
|
-
...
|
|
532
|
-
ready:
|
|
450
|
+
...createOrderDataStatus(accountId, venue, "active"),
|
|
451
|
+
ready: getSnapshotCount(record) > 0,
|
|
533
452
|
runtimeStatus: "bootstrap_pending",
|
|
534
453
|
reason: undefined,
|
|
535
454
|
lastReceivedAt: record.status.lastReceivedAt,
|
|
@@ -564,7 +483,7 @@ export class OrderManagerImpl
|
|
|
564
483
|
for (const lookupKey of getOrderLookupKeys(update)) {
|
|
565
484
|
openSetKeys.add(lookupKey);
|
|
566
485
|
}
|
|
567
|
-
const current =
|
|
486
|
+
const current = getExistingSnapshot(record, update);
|
|
568
487
|
const nextSnapshot = this.applyUpdateToRecord(
|
|
569
488
|
record,
|
|
570
489
|
accountId,
|
|
@@ -586,7 +505,7 @@ export class OrderManagerImpl
|
|
|
586
505
|
}
|
|
587
506
|
}
|
|
588
507
|
|
|
589
|
-
const disappeared =
|
|
508
|
+
const disappeared = getOpenOrderSnapshots(record).filter((order) => {
|
|
590
509
|
if (!isOpenOrder(order)) {
|
|
591
510
|
return false;
|
|
592
511
|
}
|
|
@@ -605,7 +524,7 @@ export class OrderManagerImpl
|
|
|
605
524
|
});
|
|
606
525
|
});
|
|
607
526
|
|
|
608
|
-
const orderedSnapshots =
|
|
527
|
+
const orderedSnapshots = getAllSnapshots(record);
|
|
609
528
|
const latestTs = Math.max(
|
|
610
529
|
snapshot.snapshotReceivedAt,
|
|
611
530
|
orderedSnapshots.reduce(
|
|
@@ -741,45 +660,13 @@ export class OrderManagerImpl
|
|
|
741
660
|
orderIdOnlyIndex: new Map(),
|
|
742
661
|
clientOrderIdIndex: new Map(),
|
|
743
662
|
pendingClientOrderIdIndex: new Map(),
|
|
744
|
-
status:
|
|
663
|
+
status: createOrderDataStatus(accountId, venue, "inactive"),
|
|
745
664
|
};
|
|
746
665
|
|
|
747
666
|
this.records.set(accountId, record);
|
|
748
667
|
return record;
|
|
749
668
|
}
|
|
750
669
|
|
|
751
|
-
private createStatus(
|
|
752
|
-
accountId: string,
|
|
753
|
-
venue: Venue,
|
|
754
|
-
activity: "active" | "inactive",
|
|
755
|
-
): OrderDataStatus {
|
|
756
|
-
return {
|
|
757
|
-
accountId,
|
|
758
|
-
venue,
|
|
759
|
-
activity,
|
|
760
|
-
ready: false,
|
|
761
|
-
runtimeStatus: activity === "active" ? "bootstrap_pending" : "stopped",
|
|
762
|
-
};
|
|
763
|
-
}
|
|
764
|
-
|
|
765
|
-
private getExistingSnapshot(
|
|
766
|
-
record: OrderRecord,
|
|
767
|
-
update: { symbol: string; orderId?: string; clientOrderId?: string },
|
|
768
|
-
): OrderSnapshot | undefined {
|
|
769
|
-
const location = this.getExistingSnapshotLocation(record, update);
|
|
770
|
-
return location ? this.getSnapshotAtLocation(record, location) : undefined;
|
|
771
|
-
}
|
|
772
|
-
|
|
773
|
-
private getExistingSnapshotLocation(
|
|
774
|
-
record: OrderRecord,
|
|
775
|
-
update: { symbol: string; orderId?: string; clientOrderId?: string },
|
|
776
|
-
): OrderLocation | undefined {
|
|
777
|
-
const resolution = this.resolveLocalOrderIdForUpdate(record, update);
|
|
778
|
-
return resolution.localOrderId
|
|
779
|
-
? record.localOrderLocations.get(resolution.localOrderId)
|
|
780
|
-
: undefined;
|
|
781
|
-
}
|
|
782
|
-
|
|
783
670
|
private resolveLocalOrderIdForUpdate(
|
|
784
671
|
record: OrderRecord,
|
|
785
672
|
update: { symbol: string; orderId?: string; clientOrderId?: string },
|
|
@@ -788,203 +675,40 @@ export class OrderManagerImpl
|
|
|
788
675
|
localOrderId?: string;
|
|
789
676
|
source?: "exact" | "pending" | "provisional" | "preferred";
|
|
790
677
|
} {
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
update.
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
}
|
|
801
|
-
|
|
802
|
-
if (preferredLocalOrderId) {
|
|
803
|
-
return { localOrderId: preferredLocalOrderId, source: "preferred" };
|
|
804
|
-
}
|
|
805
|
-
|
|
806
|
-
if (update.clientOrderId) {
|
|
807
|
-
const pending = record.pendingClientOrderIdIndex.get(
|
|
808
|
-
update.clientOrderId,
|
|
809
|
-
);
|
|
810
|
-
if (pending?.symbol === update.symbol) {
|
|
811
|
-
return { localOrderId: pending.localOrderId, source: "pending" };
|
|
812
|
-
}
|
|
813
|
-
}
|
|
814
|
-
|
|
815
|
-
if (
|
|
816
|
-
update.clientOrderId &&
|
|
817
|
-
!this.isSystemClientOrderId(update.clientOrderId)
|
|
818
|
-
) {
|
|
819
|
-
for (const localOrderId of record.clientOrderIdIndex.get(
|
|
820
|
-
update.clientOrderId,
|
|
821
|
-
) ?? []) {
|
|
822
|
-
const snapshot = this.getSnapshotByLocalOrderId(record, localOrderId);
|
|
823
|
-
if (snapshot && shouldMatchStoredOrderIdentity(snapshot, update)) {
|
|
824
|
-
return { localOrderId, source: "provisional" };
|
|
825
|
-
}
|
|
826
|
-
}
|
|
827
|
-
}
|
|
828
|
-
|
|
829
|
-
return {};
|
|
678
|
+
const pending =
|
|
679
|
+
update.clientOrderId === undefined
|
|
680
|
+
? undefined
|
|
681
|
+
: record.pendingClientOrderIdIndex.get(update.clientOrderId);
|
|
682
|
+
|
|
683
|
+
return resolveLocalOrderIdForUpdate(record, update, {
|
|
684
|
+
preferredLocalOrderId,
|
|
685
|
+
pendingLocalOrderId:
|
|
686
|
+
pending?.symbol === update.symbol ? pending.localOrderId : undefined,
|
|
687
|
+
});
|
|
830
688
|
}
|
|
831
689
|
|
|
832
|
-
private
|
|
690
|
+
private writeSnapshot(
|
|
833
691
|
record: OrderRecord,
|
|
834
692
|
localOrderId: string,
|
|
835
693
|
snapshot: OrderSnapshot,
|
|
836
694
|
previousLocation?: OrderLocation,
|
|
837
|
-
):
|
|
695
|
+
): boolean {
|
|
838
696
|
if (!snapshot.orderId && !snapshot.clientOrderId) {
|
|
839
697
|
this.warnDroppedUnkeyedTerminalOrder(record, snapshot);
|
|
840
|
-
return
|
|
841
|
-
}
|
|
842
|
-
|
|
843
|
-
const currentLocation =
|
|
844
|
-
previousLocation ?? record.localOrderLocations.get(localOrderId);
|
|
845
|
-
if (currentLocation) {
|
|
846
|
-
return this.moveSnapshot(record, currentLocation, localOrderId, snapshot);
|
|
847
|
-
}
|
|
848
|
-
|
|
849
|
-
return this.insertSnapshot(record, localOrderId, snapshot);
|
|
850
|
-
}
|
|
851
|
-
|
|
852
|
-
private insertSnapshot(
|
|
853
|
-
record: OrderRecord,
|
|
854
|
-
localOrderId: string,
|
|
855
|
-
snapshot: OrderSnapshot,
|
|
856
|
-
): OrderLocation | undefined {
|
|
857
|
-
const existingLocation = record.localOrderLocations.get(localOrderId);
|
|
858
|
-
if (existingLocation) {
|
|
859
|
-
this.deleteSnapshot(record, existingLocation);
|
|
860
|
-
}
|
|
861
|
-
|
|
862
|
-
const location: OrderLocation = {
|
|
863
|
-
table: isOpenOrder(snapshot) ? "open" : "closed",
|
|
864
|
-
symbol: snapshot.symbol,
|
|
865
|
-
localOrderId,
|
|
866
|
-
};
|
|
867
|
-
|
|
868
|
-
const table = this.getOrderTable(record, location.table);
|
|
869
|
-
const symbolOrders = this.getOrCreateSymbolOrders(table, location.symbol);
|
|
870
|
-
symbolOrders.set(localOrderId, snapshot);
|
|
871
|
-
record.localOrderLocations.set(localOrderId, location);
|
|
872
|
-
|
|
873
|
-
if (snapshot.orderId) {
|
|
874
|
-
const symbolIndex = this.getOrCreateOrderIdSymbolIndex(
|
|
875
|
-
record,
|
|
876
|
-
snapshot.symbol,
|
|
877
|
-
);
|
|
878
|
-
symbolIndex.set(snapshot.orderId, localOrderId);
|
|
879
|
-
this.addLocalOrderIdToSetIndex(
|
|
880
|
-
record.orderIdOnlyIndex,
|
|
881
|
-
snapshot.orderId,
|
|
882
|
-
localOrderId,
|
|
883
|
-
);
|
|
698
|
+
return false;
|
|
884
699
|
}
|
|
885
700
|
|
|
886
|
-
|
|
887
|
-
this.
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
701
|
+
const result = setSnapshot(record, localOrderId, snapshot, {
|
|
702
|
+
maxClosedOrdersPerSymbol: this.maxClosedOrdersPerSymbol,
|
|
703
|
+
previousLocation,
|
|
704
|
+
});
|
|
705
|
+
if (!result.location) {
|
|
706
|
+
return false;
|
|
892
707
|
}
|
|
893
708
|
|
|
894
|
-
this.trimClosedOrdersForSymbol(record, location);
|
|
895
709
|
this.warnSystemClientOrderIdOnlyClaim(record, snapshot);
|
|
896
710
|
this.warnProvisionalTerminalOrder(record, snapshot);
|
|
897
|
-
return
|
|
898
|
-
}
|
|
899
|
-
|
|
900
|
-
private deleteSnapshot(
|
|
901
|
-
record: OrderRecord,
|
|
902
|
-
location: OrderLocation,
|
|
903
|
-
): OrderSnapshot | undefined {
|
|
904
|
-
const snapshot = this.getSnapshotAtLocation(record, location);
|
|
905
|
-
if (!snapshot) {
|
|
906
|
-
return undefined;
|
|
907
|
-
}
|
|
908
|
-
|
|
909
|
-
const table = this.getOrderTable(record, location.table);
|
|
910
|
-
const symbolOrders = table.get(location.symbol);
|
|
911
|
-
symbolOrders?.delete(location.localOrderId);
|
|
912
|
-
if (symbolOrders?.size === 0) {
|
|
913
|
-
table.delete(location.symbol);
|
|
914
|
-
}
|
|
915
|
-
record.localOrderLocations.delete(location.localOrderId);
|
|
916
|
-
|
|
917
|
-
if (snapshot.orderId) {
|
|
918
|
-
const symbolIndex = record.orderIdIndex.get(location.symbol);
|
|
919
|
-
if (
|
|
920
|
-
symbolIndex?.get(snapshot.orderId) &&
|
|
921
|
-
symbolIndex.get(snapshot.orderId) === location.localOrderId
|
|
922
|
-
) {
|
|
923
|
-
symbolIndex.delete(snapshot.orderId);
|
|
924
|
-
}
|
|
925
|
-
if (symbolIndex?.size === 0) {
|
|
926
|
-
record.orderIdIndex.delete(location.symbol);
|
|
927
|
-
}
|
|
928
|
-
this.removeLocalOrderIdFromSetIndex(
|
|
929
|
-
record.orderIdOnlyIndex,
|
|
930
|
-
snapshot.orderId,
|
|
931
|
-
location.localOrderId,
|
|
932
|
-
);
|
|
933
|
-
}
|
|
934
|
-
|
|
935
|
-
if (snapshot.clientOrderId) {
|
|
936
|
-
this.removeLocalOrderIdFromSetIndex(
|
|
937
|
-
record.clientOrderIdIndex,
|
|
938
|
-
snapshot.clientOrderId,
|
|
939
|
-
location.localOrderId,
|
|
940
|
-
);
|
|
941
|
-
}
|
|
942
|
-
|
|
943
|
-
return snapshot;
|
|
944
|
-
}
|
|
945
|
-
|
|
946
|
-
private moveSnapshot(
|
|
947
|
-
record: OrderRecord,
|
|
948
|
-
previousLocation: OrderLocation,
|
|
949
|
-
localOrderId: string,
|
|
950
|
-
snapshot: OrderSnapshot,
|
|
951
|
-
): OrderLocation | undefined {
|
|
952
|
-
this.deleteSnapshot(record, previousLocation);
|
|
953
|
-
return this.insertSnapshot(record, localOrderId, snapshot);
|
|
954
|
-
}
|
|
955
|
-
|
|
956
|
-
private trimClosedOrdersForSymbol(
|
|
957
|
-
record: OrderRecord,
|
|
958
|
-
location: OrderLocation,
|
|
959
|
-
): void {
|
|
960
|
-
if (location.table !== "closed") {
|
|
961
|
-
return;
|
|
962
|
-
}
|
|
963
|
-
|
|
964
|
-
let symbolOrders = record.closedOrders.get(location.symbol);
|
|
965
|
-
if (!symbolOrders || symbolOrders.size <= this.maxClosedOrdersPerSymbol) {
|
|
966
|
-
return;
|
|
967
|
-
}
|
|
968
|
-
|
|
969
|
-
const trimBatchSize = Math.max(
|
|
970
|
-
1,
|
|
971
|
-
Math.floor(this.maxClosedOrdersPerSymbol / 10),
|
|
972
|
-
);
|
|
973
|
-
while (symbolOrders && symbolOrders.size > this.maxClosedOrdersPerSymbol) {
|
|
974
|
-
const keys = symbolOrders.keys();
|
|
975
|
-
for (let deleted = 0; deleted < trimBatchSize; deleted += 1) {
|
|
976
|
-
const next = keys.next();
|
|
977
|
-
if (next.done) {
|
|
978
|
-
break;
|
|
979
|
-
}
|
|
980
|
-
this.deleteSnapshot(record, {
|
|
981
|
-
table: "closed",
|
|
982
|
-
symbol: location.symbol,
|
|
983
|
-
localOrderId: next.value,
|
|
984
|
-
});
|
|
985
|
-
}
|
|
986
|
-
symbolOrders = record.closedOrders.get(location.symbol);
|
|
987
|
-
}
|
|
711
|
+
return true;
|
|
988
712
|
}
|
|
989
713
|
|
|
990
714
|
private warnDroppedUnkeyedTerminalOrder(
|
|
@@ -1015,7 +739,7 @@ export class OrderManagerImpl
|
|
|
1015
739
|
if (
|
|
1016
740
|
snapshot.orderId ||
|
|
1017
741
|
!snapshot.clientOrderId ||
|
|
1018
|
-
!
|
|
742
|
+
!isSystemClientOrderId(snapshot.clientOrderId)
|
|
1019
743
|
) {
|
|
1020
744
|
return;
|
|
1021
745
|
}
|
|
@@ -1058,211 +782,6 @@ export class OrderManagerImpl
|
|
|
1058
782
|
);
|
|
1059
783
|
}
|
|
1060
784
|
|
|
1061
|
-
private getSnapshotAtLocation(
|
|
1062
|
-
record: OrderRecord,
|
|
1063
|
-
location: OrderLocation,
|
|
1064
|
-
): OrderSnapshot | undefined {
|
|
1065
|
-
return this.getOrderTable(record, location.table)
|
|
1066
|
-
.get(location.symbol)
|
|
1067
|
-
?.get(location.localOrderId);
|
|
1068
|
-
}
|
|
1069
|
-
|
|
1070
|
-
private getSnapshotByLocalOrderId(
|
|
1071
|
-
record: OrderRecord,
|
|
1072
|
-
localOrderId: string,
|
|
1073
|
-
): OrderSnapshot | undefined {
|
|
1074
|
-
const location = record.localOrderLocations.get(localOrderId);
|
|
1075
|
-
return location ? this.getSnapshotAtLocation(record, location) : undefined;
|
|
1076
|
-
}
|
|
1077
|
-
|
|
1078
|
-
private getOrderTable(
|
|
1079
|
-
record: OrderRecord,
|
|
1080
|
-
table: OrderTable,
|
|
1081
|
-
): Map<string, Map<string, OrderSnapshot>> {
|
|
1082
|
-
return table === "open" ? record.openOrders : record.closedOrders;
|
|
1083
|
-
}
|
|
1084
|
-
|
|
1085
|
-
private getOrCreateSymbolOrders(
|
|
1086
|
-
table: Map<string, Map<string, OrderSnapshot>>,
|
|
1087
|
-
symbol: string,
|
|
1088
|
-
): Map<string, OrderSnapshot> {
|
|
1089
|
-
const existing = table.get(symbol);
|
|
1090
|
-
if (existing) {
|
|
1091
|
-
return existing;
|
|
1092
|
-
}
|
|
1093
|
-
|
|
1094
|
-
const created = new Map<string, OrderSnapshot>();
|
|
1095
|
-
table.set(symbol, created);
|
|
1096
|
-
return created;
|
|
1097
|
-
}
|
|
1098
|
-
|
|
1099
|
-
private getOrCreateOrderIdSymbolIndex(
|
|
1100
|
-
record: OrderRecord,
|
|
1101
|
-
symbol: string,
|
|
1102
|
-
): Map<string, string> {
|
|
1103
|
-
const existing = record.orderIdIndex.get(symbol);
|
|
1104
|
-
if (existing) {
|
|
1105
|
-
return existing;
|
|
1106
|
-
}
|
|
1107
|
-
|
|
1108
|
-
const created = new Map<string, string>();
|
|
1109
|
-
record.orderIdIndex.set(symbol, created);
|
|
1110
|
-
return created;
|
|
1111
|
-
}
|
|
1112
|
-
|
|
1113
|
-
private getLocalOrderIdForVenueOrderId(
|
|
1114
|
-
record: OrderRecord,
|
|
1115
|
-
symbol: string,
|
|
1116
|
-
orderId: string,
|
|
1117
|
-
): string | undefined {
|
|
1118
|
-
return record.orderIdIndex.get(symbol)?.get(orderId);
|
|
1119
|
-
}
|
|
1120
|
-
|
|
1121
|
-
private getSnapshotsForOrderId(
|
|
1122
|
-
record: OrderRecord,
|
|
1123
|
-
orderId: string,
|
|
1124
|
-
): OrderSnapshot[] {
|
|
1125
|
-
return this.getSnapshotsForLocalOrderIds(
|
|
1126
|
-
record,
|
|
1127
|
-
record.orderIdOnlyIndex.get(orderId),
|
|
1128
|
-
);
|
|
1129
|
-
}
|
|
1130
|
-
|
|
1131
|
-
private getSnapshotsForClientOrderId(
|
|
1132
|
-
record: OrderRecord,
|
|
1133
|
-
clientOrderId: string,
|
|
1134
|
-
): OrderSnapshot[] {
|
|
1135
|
-
return this.getSnapshotsForLocalOrderIds(
|
|
1136
|
-
record,
|
|
1137
|
-
record.clientOrderIdIndex.get(clientOrderId),
|
|
1138
|
-
);
|
|
1139
|
-
}
|
|
1140
|
-
|
|
1141
|
-
private getSnapshotsForLocalOrderIds(
|
|
1142
|
-
record: OrderRecord,
|
|
1143
|
-
localOrderIds?: Iterable<string>,
|
|
1144
|
-
): OrderSnapshot[] {
|
|
1145
|
-
if (!localOrderIds) {
|
|
1146
|
-
return [];
|
|
1147
|
-
}
|
|
1148
|
-
|
|
1149
|
-
const snapshots: OrderSnapshot[] = [];
|
|
1150
|
-
for (const localOrderId of localOrderIds) {
|
|
1151
|
-
const snapshot = this.getSnapshotByLocalOrderId(record, localOrderId);
|
|
1152
|
-
if (snapshot) {
|
|
1153
|
-
snapshots.push(snapshot);
|
|
1154
|
-
}
|
|
1155
|
-
}
|
|
1156
|
-
|
|
1157
|
-
return snapshots;
|
|
1158
|
-
}
|
|
1159
|
-
|
|
1160
|
-
private getOpenOrderSnapshots(record: OrderRecord): OrderSnapshot[] {
|
|
1161
|
-
return this.getSnapshotsInTable(record.openOrders);
|
|
1162
|
-
}
|
|
1163
|
-
|
|
1164
|
-
private getAllSnapshots(record: OrderRecord): OrderSnapshot[] {
|
|
1165
|
-
return [
|
|
1166
|
-
...this.getSnapshotsInTable(record.openOrders),
|
|
1167
|
-
...this.getSnapshotsInTable(record.closedOrders),
|
|
1168
|
-
];
|
|
1169
|
-
}
|
|
1170
|
-
|
|
1171
|
-
private getSnapshotsInTable(
|
|
1172
|
-
table: Map<string, Map<string, OrderSnapshot>>,
|
|
1173
|
-
): OrderSnapshot[] {
|
|
1174
|
-
const snapshots: OrderSnapshot[] = [];
|
|
1175
|
-
for (const symbolOrders of table.values()) {
|
|
1176
|
-
snapshots.push(...symbolOrders.values());
|
|
1177
|
-
}
|
|
1178
|
-
|
|
1179
|
-
return snapshots;
|
|
1180
|
-
}
|
|
1181
|
-
|
|
1182
|
-
private getSnapshotCount(record: OrderRecord): number {
|
|
1183
|
-
return (
|
|
1184
|
-
this.getSnapshotCountInTable(record.openOrders) +
|
|
1185
|
-
this.getSnapshotCountInTable(record.closedOrders)
|
|
1186
|
-
);
|
|
1187
|
-
}
|
|
1188
|
-
|
|
1189
|
-
private getSnapshotCountInTable(
|
|
1190
|
-
table: Map<string, Map<string, OrderSnapshot>>,
|
|
1191
|
-
): number {
|
|
1192
|
-
let size = 0;
|
|
1193
|
-
for (const symbolOrders of table.values()) {
|
|
1194
|
-
size += symbolOrders.size;
|
|
1195
|
-
}
|
|
1196
|
-
|
|
1197
|
-
return size;
|
|
1198
|
-
}
|
|
1199
|
-
|
|
1200
|
-
private addLocalOrderIdToSetIndex(
|
|
1201
|
-
index: Map<string, Set<string>>,
|
|
1202
|
-
key: string,
|
|
1203
|
-
localOrderId: string,
|
|
1204
|
-
): void {
|
|
1205
|
-
this.removeLocalOrderIdFromSetIndex(index, key, localOrderId);
|
|
1206
|
-
|
|
1207
|
-
const localOrderIds = index.get(key);
|
|
1208
|
-
if (localOrderIds) {
|
|
1209
|
-
localOrderIds.add(localOrderId);
|
|
1210
|
-
return;
|
|
1211
|
-
}
|
|
1212
|
-
|
|
1213
|
-
index.set(key, new Set([localOrderId]));
|
|
1214
|
-
}
|
|
1215
|
-
|
|
1216
|
-
private removeLocalOrderIdFromSetIndex(
|
|
1217
|
-
index: Map<string, Set<string>>,
|
|
1218
|
-
key: string,
|
|
1219
|
-
localOrderId: string,
|
|
1220
|
-
): void {
|
|
1221
|
-
const localOrderIds = index.get(key);
|
|
1222
|
-
if (!localOrderIds) {
|
|
1223
|
-
return;
|
|
1224
|
-
}
|
|
1225
|
-
|
|
1226
|
-
localOrderIds.delete(localOrderId);
|
|
1227
|
-
|
|
1228
|
-
if (localOrderIds.size === 0) {
|
|
1229
|
-
index.delete(key);
|
|
1230
|
-
}
|
|
1231
|
-
}
|
|
1232
|
-
|
|
1233
|
-
private selectLatestSnapshot(
|
|
1234
|
-
snapshots: OrderSnapshot[],
|
|
1235
|
-
): OrderSnapshot | undefined {
|
|
1236
|
-
let latest: OrderSnapshot | undefined;
|
|
1237
|
-
for (const snapshot of snapshots) {
|
|
1238
|
-
if (!latest) {
|
|
1239
|
-
latest = snapshot;
|
|
1240
|
-
continue;
|
|
1241
|
-
}
|
|
1242
|
-
|
|
1243
|
-
const snapshotOpen = isOpenOrder(snapshot);
|
|
1244
|
-
const latestOpen = isOpenOrder(latest);
|
|
1245
|
-
if (snapshotOpen !== latestOpen) {
|
|
1246
|
-
// Open candidate has absolute priority: current active order takes
|
|
1247
|
-
// precedence over historical terminal state (when clientOrderId is
|
|
1248
|
-
// reused, the old order is already closed).
|
|
1249
|
-
if (snapshotOpen) {
|
|
1250
|
-
latest = snapshot;
|
|
1251
|
-
}
|
|
1252
|
-
continue;
|
|
1253
|
-
}
|
|
1254
|
-
|
|
1255
|
-
// Both open or both closed: take the latest by updatedAt.
|
|
1256
|
-
// seq must not be used -- seq is a per-order version number and is not
|
|
1257
|
-
// comparable across orders (e.g. different orders that reuse a cid).
|
|
1258
|
-
if (snapshot.updatedAt > latest.updatedAt) {
|
|
1259
|
-
latest = snapshot;
|
|
1260
|
-
}
|
|
1261
|
-
}
|
|
1262
|
-
|
|
1263
|
-
return latest;
|
|
1264
|
-
}
|
|
1265
|
-
|
|
1266
785
|
private applyUpdateToRecord(
|
|
1267
786
|
record: OrderRecord,
|
|
1268
787
|
accountId: string,
|
|
@@ -1272,9 +791,9 @@ export class OrderManagerImpl
|
|
|
1272
791
|
): OrderSnapshot | undefined {
|
|
1273
792
|
const resolution = this.resolveLocalOrderIdForUpdate(record, update);
|
|
1274
793
|
const localOrderId = resolution.localOrderId ?? this.generateLocalOrderId();
|
|
1275
|
-
const previousLocation = record
|
|
794
|
+
const previousLocation = getLocationByLocalOrderId(record, localOrderId);
|
|
1276
795
|
const previous = previousLocation
|
|
1277
|
-
?
|
|
796
|
+
? getSnapshotAtLocation(record, previousLocation)
|
|
1278
797
|
: undefined;
|
|
1279
798
|
if (
|
|
1280
799
|
!shouldApplyWatermarkedUpdate(previous, update, {
|
|
@@ -1285,14 +804,14 @@ export class OrderManagerImpl
|
|
|
1285
804
|
return undefined;
|
|
1286
805
|
}
|
|
1287
806
|
|
|
1288
|
-
const snapshot =
|
|
1289
|
-
const
|
|
807
|
+
const snapshot = createSnapshot(accountId, venue, update, previous);
|
|
808
|
+
const written = this.writeSnapshot(
|
|
1290
809
|
record,
|
|
1291
810
|
localOrderId,
|
|
1292
811
|
snapshot,
|
|
1293
812
|
previousLocation,
|
|
1294
813
|
);
|
|
1295
|
-
if (
|
|
814
|
+
if (written && resolution.source === "pending" && update.clientOrderId) {
|
|
1296
815
|
this.clearPendingClientOrderClaim(
|
|
1297
816
|
record,
|
|
1298
817
|
update.clientOrderId,
|
|
@@ -1300,77 +819,7 @@ export class OrderManagerImpl
|
|
|
1300
819
|
);
|
|
1301
820
|
}
|
|
1302
821
|
|
|
1303
|
-
return
|
|
1304
|
-
}
|
|
1305
|
-
|
|
1306
|
-
private createSnapshot(
|
|
1307
|
-
accountId: string,
|
|
1308
|
-
venue: Venue,
|
|
1309
|
-
input: RawOrderUpdate,
|
|
1310
|
-
previous?: OrderSnapshot,
|
|
1311
|
-
): OrderSnapshot {
|
|
1312
|
-
const amount = new BigNumber(input.amount);
|
|
1313
|
-
const rawFilled = new BigNumber(input.filled);
|
|
1314
|
-
const filled =
|
|
1315
|
-
previous &&
|
|
1316
|
-
input.exchangeTs !== undefined &&
|
|
1317
|
-
previous.exchangeTs === input.exchangeTs
|
|
1318
|
-
? BigNumber.maximum(rawFilled, previous.filled)
|
|
1319
|
-
: rawFilled;
|
|
1320
|
-
const remaining =
|
|
1321
|
-
input.remaining === undefined
|
|
1322
|
-
? amount.minus(filled)
|
|
1323
|
-
: new BigNumber(input.remaining);
|
|
1324
|
-
|
|
1325
|
-
return {
|
|
1326
|
-
accountId,
|
|
1327
|
-
venue,
|
|
1328
|
-
orderId: input.orderId ?? previous?.orderId,
|
|
1329
|
-
clientOrderId: input.clientOrderId ?? previous?.clientOrderId,
|
|
1330
|
-
symbol: input.symbol,
|
|
1331
|
-
side: input.side,
|
|
1332
|
-
type: input.type,
|
|
1333
|
-
status: this.mergeOrderStatus(input, previous),
|
|
1334
|
-
price:
|
|
1335
|
-
input.price === undefined ? previous?.price : toCanonical(input.price),
|
|
1336
|
-
triggerPrice:
|
|
1337
|
-
input.triggerPrice === undefined
|
|
1338
|
-
? previous?.triggerPrice
|
|
1339
|
-
: toCanonical(input.triggerPrice),
|
|
1340
|
-
amount: toCanonical(amount),
|
|
1341
|
-
filled: toCanonical(filled),
|
|
1342
|
-
remaining: toCanonical(remaining),
|
|
1343
|
-
reduceOnly: input.reduceOnly ?? previous?.reduceOnly,
|
|
1344
|
-
positionSide: input.positionSide ?? previous?.positionSide,
|
|
1345
|
-
avgFillPrice:
|
|
1346
|
-
input.avgFillPrice === undefined
|
|
1347
|
-
? previous?.avgFillPrice
|
|
1348
|
-
: toCanonical(input.avgFillPrice),
|
|
1349
|
-
exchangeTs: input.exchangeTs,
|
|
1350
|
-
receivedAt: input.receivedAt,
|
|
1351
|
-
updatedAt: input.receivedAt,
|
|
1352
|
-
seq: (previous?.seq ?? 0) + 1,
|
|
1353
|
-
};
|
|
1354
|
-
}
|
|
1355
|
-
|
|
1356
|
-
private mergeOrderStatus(
|
|
1357
|
-
input: RawOrderUpdate,
|
|
1358
|
-
previous?: OrderSnapshot,
|
|
1359
|
-
): OrderSnapshot["status"] {
|
|
1360
|
-
if (!previous) {
|
|
1361
|
-
return input.status;
|
|
1362
|
-
}
|
|
1363
|
-
|
|
1364
|
-
if (
|
|
1365
|
-
input.exchangeTs !== undefined &&
|
|
1366
|
-
previous.exchangeTs !== undefined &&
|
|
1367
|
-
input.exchangeTs === previous.exchangeTs &&
|
|
1368
|
-
orderPriority(input.status) < orderPriority(previous.status)
|
|
1369
|
-
) {
|
|
1370
|
-
return previous.status;
|
|
1371
|
-
}
|
|
1372
|
-
|
|
1373
|
-
return input.status;
|
|
822
|
+
return written ? snapshot : undefined;
|
|
1374
823
|
}
|
|
1375
824
|
|
|
1376
825
|
private publishStatus(record: OrderRecord): void {
|
|
@@ -1395,10 +844,7 @@ export class OrderManagerImpl
|
|
|
1395
844
|
if (
|
|
1396
845
|
(options?.record &&
|
|
1397
846
|
options.avoidOpenClientOrderId &&
|
|
1398
|
-
|
|
1399
|
-
options.record,
|
|
1400
|
-
candidate,
|
|
1401
|
-
)) ||
|
|
847
|
+
isVenueClientOrderIdInUseForOpenOrder(options.record, candidate)) ||
|
|
1402
848
|
options?.record?.pendingClientOrderIdIndex.has(candidate) ||
|
|
1403
849
|
!VENUE_CLIENT_ORDER_ID_PATTERN.test(candidate)
|
|
1404
850
|
) {
|
|
@@ -1409,22 +855,6 @@ export class OrderManagerImpl
|
|
|
1409
855
|
}
|
|
1410
856
|
}
|
|
1411
857
|
|
|
1412
|
-
private isVenueClientOrderIdInUseForOpenOrder(
|
|
1413
|
-
record: OrderRecord,
|
|
1414
|
-
venueClientOrderId: string,
|
|
1415
|
-
): boolean {
|
|
1416
|
-
for (const localOrderId of record.clientOrderIdIndex.get(
|
|
1417
|
-
venueClientOrderId,
|
|
1418
|
-
) ?? []) {
|
|
1419
|
-
const location = record.localOrderLocations.get(localOrderId);
|
|
1420
|
-
if (location?.table === "open") {
|
|
1421
|
-
return true;
|
|
1422
|
-
}
|
|
1423
|
-
}
|
|
1424
|
-
|
|
1425
|
-
return false;
|
|
1426
|
-
}
|
|
1427
|
-
|
|
1428
858
|
private addPendingClientOrderClaim(
|
|
1429
859
|
record: OrderRecord,
|
|
1430
860
|
symbol: string,
|
|
@@ -1452,12 +882,6 @@ export class OrderManagerImpl
|
|
|
1452
882
|
return isTransportError(error) && error.kind === "timeout";
|
|
1453
883
|
}
|
|
1454
884
|
|
|
1455
|
-
private isSystemClientOrderId(clientOrderId: string): boolean {
|
|
1456
|
-
return SYSTEM_CLIENT_ORDER_ID_PATTERNS.some((pattern) =>
|
|
1457
|
-
pattern.test(clientOrderId),
|
|
1458
|
-
);
|
|
1459
|
-
}
|
|
1460
|
-
|
|
1461
885
|
private validateCreateOrderInput(
|
|
1462
886
|
input: CreateOrderInput,
|
|
1463
887
|
venue: Venue,
|
|
@@ -1513,8 +937,8 @@ export class OrderManagerImpl
|
|
|
1513
937
|
accountId: string,
|
|
1514
938
|
venue: Venue,
|
|
1515
939
|
update: RawOrderUpdate,
|
|
1516
|
-
options: { localOrderId?: string } = {},
|
|
1517
|
-
): OrderSnapshot {
|
|
940
|
+
options: { localOrderId?: string; requestStartedAt?: number } = {},
|
|
941
|
+
): OrderSnapshot | undefined {
|
|
1518
942
|
const record = this.getOrCreateRecord(accountId, venue);
|
|
1519
943
|
const resolution = this.resolveLocalOrderIdForUpdate(
|
|
1520
944
|
record,
|
|
@@ -1522,23 +946,47 @@ export class OrderManagerImpl
|
|
|
1522
946
|
options.localOrderId,
|
|
1523
947
|
);
|
|
1524
948
|
const localOrderId = resolution.localOrderId ?? this.generateLocalOrderId();
|
|
1525
|
-
const previousLocation = record
|
|
949
|
+
const previousLocation = getLocationByLocalOrderId(record, localOrderId);
|
|
1526
950
|
const previous = previousLocation
|
|
1527
|
-
?
|
|
951
|
+
? getSnapshotAtLocation(record, previousLocation)
|
|
952
|
+
: undefined;
|
|
953
|
+
if (
|
|
954
|
+
previous &&
|
|
955
|
+
!shouldApplyWatermarkedUpdate(previous, update, {
|
|
956
|
+
requestStartedAt: options.requestStartedAt,
|
|
957
|
+
source: "command",
|
|
958
|
+
})
|
|
959
|
+
) {
|
|
960
|
+
return previous;
|
|
961
|
+
}
|
|
962
|
+
|
|
963
|
+
const snapshot = createSnapshot(accountId, venue, update, previous);
|
|
964
|
+
return this.writeSnapshot(record, localOrderId, snapshot, previousLocation)
|
|
965
|
+
? snapshot
|
|
1528
966
|
: undefined;
|
|
1529
|
-
const snapshot = this.createSnapshot(accountId, venue, update, previous);
|
|
1530
|
-
this.setSnapshot(record, localOrderId, snapshot, previousLocation);
|
|
1531
|
-
return snapshot;
|
|
1532
967
|
}
|
|
1533
968
|
|
|
1534
969
|
private applyCommandUpdates(
|
|
1535
970
|
accountId: string,
|
|
1536
971
|
venue: Venue,
|
|
1537
972
|
updates: RawOrderUpdate[],
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
)
|
|
973
|
+
options: { requestStartedAt?: number } = {},
|
|
974
|
+
): OrderSnapshot[] | undefined {
|
|
975
|
+
const snapshots: OrderSnapshot[] = [];
|
|
976
|
+
for (const update of updates) {
|
|
977
|
+
const snapshot = this.applyCommandUpdate(
|
|
978
|
+
accountId,
|
|
979
|
+
venue,
|
|
980
|
+
update,
|
|
981
|
+
options,
|
|
982
|
+
);
|
|
983
|
+
if (!snapshot) {
|
|
984
|
+
return undefined;
|
|
985
|
+
}
|
|
986
|
+
snapshots.push(snapshot);
|
|
987
|
+
}
|
|
988
|
+
|
|
989
|
+
return snapshots;
|
|
1542
990
|
}
|
|
1543
991
|
|
|
1544
992
|
private createError(
|