@drift-labs/sdk 2.17.0 → 2.18.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/lib/constants/perpMarkets.js +10 -0
- package/lib/dlob/DLOB.d.ts +32 -26
- package/lib/dlob/DLOB.js +246 -173
- package/lib/dlob/DLOBNode.d.ts +11 -5
- package/lib/dlob/DLOBNode.js +13 -5
- package/lib/driftClient.js +5 -10
- package/lib/examples/loadDlob.js +1 -1
- package/lib/factory/oracleClient.js +4 -0
- package/lib/idl/drift.json +4 -1
- package/lib/math/auction.d.ts +1 -0
- package/lib/math/auction.js +9 -2
- package/lib/math/orders.d.ts +2 -0
- package/lib/math/orders.js +11 -2
- package/lib/math/spotBalance.js +1 -1
- package/lib/math/trade.d.ts +6 -0
- package/lib/math/trade.js +6 -0
- package/lib/oracles/pythClient.d.ts +4 -3
- package/lib/oracles/pythClient.js +8 -7
- package/lib/types.d.ts +4 -1
- package/lib/types.js +1 -0
- package/lib/user.js +8 -9
- package/package.json +1 -1
- package/src/constants/perpMarkets.ts +10 -0
- package/src/dlob/DLOB.ts +387 -183
- package/src/dlob/DLOBNode.ts +20 -7
- package/src/driftClient.ts +5 -10
- package/src/examples/loadDlob.ts +1 -1
- package/src/factory/oracleClient.ts +5 -0
- package/src/idl/drift.json +4 -1
- package/src/math/auction.ts +13 -1
- package/src/math/orders.ts +14 -1
- package/src/math/spotBalance.ts +1 -1
- package/src/math/trade.ts +6 -0
- package/src/oracles/pythClient.ts +27 -8
- package/src/types.ts +2 -1
- package/src/user.ts +7 -8
- package/tests/dlob/test.ts +800 -82
package/src/dlob/DLOB.ts
CHANGED
|
@@ -6,7 +6,6 @@ import {
|
|
|
6
6
|
calculateBidPrice,
|
|
7
7
|
DriftClient,
|
|
8
8
|
convertToNumber,
|
|
9
|
-
isAuctionComplete,
|
|
10
9
|
isOrderExpired,
|
|
11
10
|
isOneOfVariant,
|
|
12
11
|
isVariant,
|
|
@@ -19,7 +18,6 @@ import {
|
|
|
19
18
|
SlotSubscriber,
|
|
20
19
|
MarketTypeStr,
|
|
21
20
|
StateAccount,
|
|
22
|
-
isMarketOrder,
|
|
23
21
|
mustBeTriggered,
|
|
24
22
|
isTriggered,
|
|
25
23
|
getLimitPrice,
|
|
@@ -28,6 +26,9 @@ import {
|
|
|
28
26
|
OrderActionRecord,
|
|
29
27
|
ZERO,
|
|
30
28
|
BN_MAX,
|
|
29
|
+
isRestingLimitOrder,
|
|
30
|
+
isTakingOrder,
|
|
31
|
+
isFallbackAvailableLiquiditySource,
|
|
31
32
|
} from '..';
|
|
32
33
|
import { PublicKey } from '@solana/web3.js';
|
|
33
34
|
import { DLOBNode, DLOBNodeType, TriggerOrderNode } from '..';
|
|
@@ -35,14 +36,18 @@ import { ammPaused, exchangePaused, fillPaused } from '../math/exchangeStatus';
|
|
|
35
36
|
import { DLOBOrders } from './DLOBOrders';
|
|
36
37
|
|
|
37
38
|
export type MarketNodeLists = {
|
|
38
|
-
|
|
39
|
-
ask: NodeList<'
|
|
40
|
-
bid: NodeList<'
|
|
39
|
+
restingLimit: {
|
|
40
|
+
ask: NodeList<'restingLimit'>;
|
|
41
|
+
bid: NodeList<'restingLimit'>;
|
|
41
42
|
};
|
|
42
43
|
floatingLimit: {
|
|
43
44
|
ask: NodeList<'floatingLimit'>;
|
|
44
45
|
bid: NodeList<'floatingLimit'>;
|
|
45
46
|
};
|
|
47
|
+
takingLimit: {
|
|
48
|
+
ask: NodeList<'takingLimit'>;
|
|
49
|
+
bid: NodeList<'takingLimit'>;
|
|
50
|
+
};
|
|
46
51
|
market: {
|
|
47
52
|
ask: NodeList<'market'>;
|
|
48
53
|
bid: NodeList<'market'>;
|
|
@@ -75,6 +80,7 @@ const SUPPORTED_ORDER_TYPES = [
|
|
|
75
80
|
export class DLOB {
|
|
76
81
|
openOrders = new Map<MarketTypeStr, Set<string>>();
|
|
77
82
|
orderLists = new Map<MarketTypeStr, Map<number, MarketNodeLists>>();
|
|
83
|
+
maxSlotForRestingLimitOrders = 0;
|
|
78
84
|
|
|
79
85
|
initialized = false;
|
|
80
86
|
|
|
@@ -109,6 +115,8 @@ export class DLOB {
|
|
|
109
115
|
}
|
|
110
116
|
this.orderLists.clear();
|
|
111
117
|
|
|
118
|
+
this.maxSlotForRestingLimitOrders = 0;
|
|
119
|
+
|
|
112
120
|
this.init();
|
|
113
121
|
}
|
|
114
122
|
|
|
@@ -117,7 +125,10 @@ export class DLOB {
|
|
|
117
125
|
*
|
|
118
126
|
* @returns a promise that resolves when the DLOB is initialized
|
|
119
127
|
*/
|
|
120
|
-
public async initFromUserMap(
|
|
128
|
+
public async initFromUserMap(
|
|
129
|
+
userMap: UserMap,
|
|
130
|
+
slot: number
|
|
131
|
+
): Promise<boolean> {
|
|
121
132
|
if (this.initialized) {
|
|
122
133
|
return false;
|
|
123
134
|
}
|
|
@@ -128,7 +139,7 @@ export class DLOB {
|
|
|
128
139
|
const userAccountPubkey = user.getUserAccountPublicKey();
|
|
129
140
|
|
|
130
141
|
for (const order of userAccount.orders) {
|
|
131
|
-
this.insertOrder(order, userAccountPubkey);
|
|
142
|
+
this.insertOrder(order, userAccountPubkey, slot);
|
|
132
143
|
}
|
|
133
144
|
}
|
|
134
145
|
|
|
@@ -136,24 +147,27 @@ export class DLOB {
|
|
|
136
147
|
return true;
|
|
137
148
|
}
|
|
138
149
|
|
|
139
|
-
public initFromOrders(dlobOrders: DLOBOrders): boolean {
|
|
150
|
+
public initFromOrders(dlobOrders: DLOBOrders, slot: number): boolean {
|
|
140
151
|
if (this.initialized) {
|
|
141
152
|
return false;
|
|
142
153
|
}
|
|
143
154
|
|
|
144
155
|
for (const { user, order } of dlobOrders) {
|
|
145
|
-
this.insertOrder(order, user);
|
|
156
|
+
this.insertOrder(order, user, slot);
|
|
146
157
|
}
|
|
147
158
|
|
|
148
159
|
this.initialized = true;
|
|
149
160
|
return true;
|
|
150
161
|
}
|
|
151
162
|
|
|
152
|
-
public handleOrderRecord(record: OrderRecord): void {
|
|
153
|
-
this.insertOrder(record.order, record.user);
|
|
163
|
+
public handleOrderRecord(record: OrderRecord, slot: number): void {
|
|
164
|
+
this.insertOrder(record.order, record.user, slot);
|
|
154
165
|
}
|
|
155
166
|
|
|
156
|
-
public handleOrderActionRecord(
|
|
167
|
+
public handleOrderActionRecord(
|
|
168
|
+
record: OrderActionRecord,
|
|
169
|
+
slot: number
|
|
170
|
+
): void {
|
|
157
171
|
if (isOneOfVariant(record.action, ['place', 'expire'])) {
|
|
158
172
|
return;
|
|
159
173
|
}
|
|
@@ -162,14 +176,14 @@ export class DLOB {
|
|
|
162
176
|
if (record.taker !== null) {
|
|
163
177
|
const takerOrder = this.getOrder(record.takerOrderId, record.taker);
|
|
164
178
|
if (takerOrder) {
|
|
165
|
-
this.trigger(takerOrder, record.taker);
|
|
179
|
+
this.trigger(takerOrder, record.taker, slot);
|
|
166
180
|
}
|
|
167
181
|
}
|
|
168
182
|
|
|
169
183
|
if (record.maker !== null) {
|
|
170
184
|
const makerOrder = this.getOrder(record.makerOrderId, record.maker);
|
|
171
185
|
if (makerOrder) {
|
|
172
|
-
this.trigger(makerOrder, record.maker);
|
|
186
|
+
this.trigger(makerOrder, record.maker, slot);
|
|
173
187
|
}
|
|
174
188
|
}
|
|
175
189
|
} else if (isVariant(record.action, 'fill')) {
|
|
@@ -179,6 +193,7 @@ export class DLOB {
|
|
|
179
193
|
this.updateOrder(
|
|
180
194
|
takerOrder,
|
|
181
195
|
record.taker,
|
|
196
|
+
slot,
|
|
182
197
|
record.takerOrderCumulativeBaseAssetAmountFilled
|
|
183
198
|
);
|
|
184
199
|
}
|
|
@@ -190,6 +205,7 @@ export class DLOB {
|
|
|
190
205
|
this.updateOrder(
|
|
191
206
|
makerOrder,
|
|
192
207
|
record.maker,
|
|
208
|
+
slot,
|
|
193
209
|
record.makerOrderCumulativeBaseAssetAmountFilled
|
|
194
210
|
);
|
|
195
211
|
}
|
|
@@ -198,14 +214,14 @@ export class DLOB {
|
|
|
198
214
|
if (record.taker !== null) {
|
|
199
215
|
const takerOrder = this.getOrder(record.takerOrderId, record.taker);
|
|
200
216
|
if (takerOrder) {
|
|
201
|
-
this.delete(takerOrder, record.taker);
|
|
217
|
+
this.delete(takerOrder, record.taker, slot);
|
|
202
218
|
}
|
|
203
219
|
}
|
|
204
220
|
|
|
205
221
|
if (record.maker !== null) {
|
|
206
222
|
const makerOrder = this.getOrder(record.makerOrderId, record.maker);
|
|
207
223
|
if (makerOrder) {
|
|
208
|
-
this.delete(makerOrder, record.maker);
|
|
224
|
+
this.delete(makerOrder, record.maker, slot);
|
|
209
225
|
}
|
|
210
226
|
}
|
|
211
227
|
}
|
|
@@ -214,6 +230,7 @@ export class DLOB {
|
|
|
214
230
|
public insertOrder(
|
|
215
231
|
order: Order,
|
|
216
232
|
userAccount: PublicKey,
|
|
233
|
+
slot: number,
|
|
217
234
|
onInsert?: OrderBookCallback
|
|
218
235
|
): void {
|
|
219
236
|
if (isVariant(order.status, 'init')) {
|
|
@@ -235,7 +252,7 @@ export class DLOB {
|
|
|
235
252
|
.get(marketType)
|
|
236
253
|
.add(getOrderSignature(order.orderId, userAccount));
|
|
237
254
|
}
|
|
238
|
-
this.getListForOrder(order)?.insert(order, marketType, userAccount);
|
|
255
|
+
this.getListForOrder(order, slot)?.insert(order, marketType, userAccount);
|
|
239
256
|
|
|
240
257
|
if (onInsert) {
|
|
241
258
|
onInsert();
|
|
@@ -244,14 +261,18 @@ export class DLOB {
|
|
|
244
261
|
|
|
245
262
|
addOrderList(marketType: MarketTypeStr, marketIndex: number): void {
|
|
246
263
|
this.orderLists.get(marketType).set(marketIndex, {
|
|
247
|
-
|
|
248
|
-
ask: new NodeList('
|
|
249
|
-
bid: new NodeList('
|
|
264
|
+
restingLimit: {
|
|
265
|
+
ask: new NodeList('restingLimit', 'asc'),
|
|
266
|
+
bid: new NodeList('restingLimit', 'desc'),
|
|
250
267
|
},
|
|
251
268
|
floatingLimit: {
|
|
252
269
|
ask: new NodeList('floatingLimit', 'asc'),
|
|
253
270
|
bid: new NodeList('floatingLimit', 'desc'),
|
|
254
271
|
},
|
|
272
|
+
takingLimit: {
|
|
273
|
+
ask: new NodeList('takingLimit', 'asc'),
|
|
274
|
+
bid: new NodeList('takingLimit', 'asc'), // always sort ascending for market orders
|
|
275
|
+
},
|
|
255
276
|
market: {
|
|
256
277
|
ask: new NodeList('market', 'asc'),
|
|
257
278
|
bid: new NodeList('market', 'asc'), // always sort ascending for market orders
|
|
@@ -266,11 +287,14 @@ export class DLOB {
|
|
|
266
287
|
public updateOrder(
|
|
267
288
|
order: Order,
|
|
268
289
|
userAccount: PublicKey,
|
|
290
|
+
slot: number,
|
|
269
291
|
cumulativeBaseAssetAmountFilled: BN,
|
|
270
292
|
onUpdate?: OrderBookCallback
|
|
271
293
|
): void {
|
|
294
|
+
this.updateRestingLimitOrders(slot);
|
|
295
|
+
|
|
272
296
|
if (order.baseAssetAmount.eq(cumulativeBaseAssetAmountFilled)) {
|
|
273
|
-
this.delete(order, userAccount);
|
|
297
|
+
this.delete(order, userAccount, slot);
|
|
274
298
|
return;
|
|
275
299
|
}
|
|
276
300
|
|
|
@@ -283,7 +307,7 @@ export class DLOB {
|
|
|
283
307
|
};
|
|
284
308
|
newOrder.baseAssetAmountFilled = cumulativeBaseAssetAmountFilled;
|
|
285
309
|
|
|
286
|
-
this.getListForOrder(order)?.update(newOrder, userAccount);
|
|
310
|
+
this.getListForOrder(order, slot)?.update(newOrder, userAccount);
|
|
287
311
|
|
|
288
312
|
if (onUpdate) {
|
|
289
313
|
onUpdate();
|
|
@@ -293,12 +317,15 @@ export class DLOB {
|
|
|
293
317
|
public trigger(
|
|
294
318
|
order: Order,
|
|
295
319
|
userAccount: PublicKey,
|
|
320
|
+
slot: number,
|
|
296
321
|
onTrigger?: OrderBookCallback
|
|
297
322
|
): void {
|
|
298
323
|
if (isVariant(order.status, 'init')) {
|
|
299
324
|
return;
|
|
300
325
|
}
|
|
301
326
|
|
|
327
|
+
this.updateRestingLimitOrders(slot);
|
|
328
|
+
|
|
302
329
|
if (isTriggered(order)) {
|
|
303
330
|
return;
|
|
304
331
|
}
|
|
@@ -309,7 +336,7 @@ export class DLOB {
|
|
|
309
336
|
.trigger[isVariant(order.triggerCondition, 'above') ? 'above' : 'below'];
|
|
310
337
|
triggerList.remove(order, userAccount);
|
|
311
338
|
|
|
312
|
-
this.getListForOrder(order)?.insert(order, marketType, userAccount);
|
|
339
|
+
this.getListForOrder(order, slot)?.insert(order, marketType, userAccount);
|
|
313
340
|
if (onTrigger) {
|
|
314
341
|
onTrigger();
|
|
315
342
|
}
|
|
@@ -318,19 +345,25 @@ export class DLOB {
|
|
|
318
345
|
public delete(
|
|
319
346
|
order: Order,
|
|
320
347
|
userAccount: PublicKey,
|
|
348
|
+
slot: number,
|
|
321
349
|
onDelete?: OrderBookCallback
|
|
322
350
|
): void {
|
|
323
351
|
if (isVariant(order.status, 'init')) {
|
|
324
352
|
return;
|
|
325
353
|
}
|
|
326
354
|
|
|
327
|
-
this.
|
|
355
|
+
this.updateRestingLimitOrders(slot);
|
|
356
|
+
|
|
357
|
+
this.getListForOrder(order, slot)?.remove(order, userAccount);
|
|
328
358
|
if (onDelete) {
|
|
329
359
|
onDelete();
|
|
330
360
|
}
|
|
331
361
|
}
|
|
332
362
|
|
|
333
|
-
public getListForOrder(
|
|
363
|
+
public getListForOrder(
|
|
364
|
+
order: Order,
|
|
365
|
+
slot: number
|
|
366
|
+
): NodeList<any> | undefined {
|
|
334
367
|
const isInactiveTriggerOrder =
|
|
335
368
|
mustBeTriggered(order) && !isTriggered(order);
|
|
336
369
|
|
|
@@ -344,7 +377,8 @@ export class DLOB {
|
|
|
344
377
|
} else if (order.oraclePriceOffset !== 0) {
|
|
345
378
|
type = 'floatingLimit';
|
|
346
379
|
} else {
|
|
347
|
-
|
|
380
|
+
const isResting = isRestingLimitOrder(order, slot);
|
|
381
|
+
type = isResting ? 'restingLimit' : 'takingLimit';
|
|
348
382
|
}
|
|
349
383
|
|
|
350
384
|
let subType: string;
|
|
@@ -365,6 +399,58 @@ export class DLOB {
|
|
|
365
399
|
];
|
|
366
400
|
}
|
|
367
401
|
|
|
402
|
+
public updateRestingLimitOrders(slot: number): void {
|
|
403
|
+
if (slot <= this.maxSlotForRestingLimitOrders) {
|
|
404
|
+
return;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
this.maxSlotForRestingLimitOrders = slot;
|
|
408
|
+
|
|
409
|
+
this.updateRestingLimitOrdersForMarketType(slot, 'perp');
|
|
410
|
+
|
|
411
|
+
this.updateRestingLimitOrdersForMarketType(slot, 'spot');
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
updateRestingLimitOrdersForMarketType(
|
|
415
|
+
slot: number,
|
|
416
|
+
marketTypeStr: MarketTypeStr
|
|
417
|
+
): void {
|
|
418
|
+
for (const [_, nodeLists] of this.orderLists.get(marketTypeStr)) {
|
|
419
|
+
const nodesToUpdate = [];
|
|
420
|
+
for (const node of nodeLists.takingLimit.ask.getGenerator()) {
|
|
421
|
+
if (!isRestingLimitOrder(node.order, slot)) {
|
|
422
|
+
continue;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
nodesToUpdate.push({
|
|
426
|
+
side: 'ask',
|
|
427
|
+
node,
|
|
428
|
+
});
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
for (const node of nodeLists.takingLimit.bid.getGenerator()) {
|
|
432
|
+
if (!isRestingLimitOrder(node.order, slot)) {
|
|
433
|
+
continue;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
nodesToUpdate.push({
|
|
437
|
+
side: 'bid',
|
|
438
|
+
node,
|
|
439
|
+
});
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
for (const nodeToUpdate of nodesToUpdate) {
|
|
443
|
+
const { side, node } = nodeToUpdate;
|
|
444
|
+
nodeLists.takingLimit[side].remove(node.order, node.userAccount);
|
|
445
|
+
nodeLists.restingLimit[side].insert(
|
|
446
|
+
node.order,
|
|
447
|
+
marketTypeStr,
|
|
448
|
+
node.userAccount
|
|
449
|
+
);
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
|
|
368
454
|
public getOrder(orderId: number, userAccount: PublicKey): Order | undefined {
|
|
369
455
|
for (const nodeList of this.getNodeLists()) {
|
|
370
456
|
const node = nodeList.get(orderId, userAccount);
|
|
@@ -393,24 +479,30 @@ export class DLOB {
|
|
|
393
479
|
|
|
394
480
|
const isAmmPaused = ammPaused(stateAccount, marketAccount);
|
|
395
481
|
|
|
396
|
-
const
|
|
397
|
-
|
|
482
|
+
const minAuctionDuration = isVariant(marketType, 'perp')
|
|
483
|
+
? stateAccount.minPerpAuctionDuration
|
|
484
|
+
: 0;
|
|
485
|
+
|
|
486
|
+
const restingLimitOrderNodesToFill: Array<NodeToFill> =
|
|
487
|
+
this.findRestingLimitOrderNodesToFill(
|
|
398
488
|
marketIndex,
|
|
399
489
|
slot,
|
|
400
490
|
marketType,
|
|
401
491
|
oraclePriceData,
|
|
402
492
|
isAmmPaused,
|
|
493
|
+
minAuctionDuration,
|
|
403
494
|
fallbackAsk,
|
|
404
495
|
fallbackBid
|
|
405
496
|
);
|
|
406
497
|
|
|
407
|
-
const
|
|
408
|
-
this.
|
|
498
|
+
const takingOrderNodesToFill: Array<NodeToFill> =
|
|
499
|
+
this.findTakingNodesToFill(
|
|
409
500
|
marketIndex,
|
|
410
501
|
slot,
|
|
411
502
|
marketType,
|
|
412
503
|
oraclePriceData,
|
|
413
504
|
isAmmPaused,
|
|
505
|
+
minAuctionDuration,
|
|
414
506
|
fallbackAsk,
|
|
415
507
|
fallbackBid
|
|
416
508
|
);
|
|
@@ -421,28 +513,30 @@ export class DLOB {
|
|
|
421
513
|
ts,
|
|
422
514
|
marketType
|
|
423
515
|
);
|
|
424
|
-
return
|
|
425
|
-
|
|
516
|
+
return restingLimitOrderNodesToFill.concat(
|
|
517
|
+
takingOrderNodesToFill,
|
|
426
518
|
expiredNodesToFill
|
|
427
519
|
);
|
|
428
520
|
}
|
|
429
521
|
|
|
430
|
-
public
|
|
522
|
+
public findRestingLimitOrderNodesToFill(
|
|
431
523
|
marketIndex: number,
|
|
432
524
|
slot: number,
|
|
433
525
|
marketType: MarketType,
|
|
434
526
|
oraclePriceData: OraclePriceData,
|
|
435
527
|
isAmmPaused: boolean,
|
|
528
|
+
minAuctionDuration: number,
|
|
436
529
|
fallbackAsk: BN | undefined,
|
|
437
530
|
fallbackBid: BN | undefined
|
|
438
531
|
): NodeToFill[] {
|
|
439
532
|
const nodesToFill = new Array<NodeToFill>();
|
|
440
533
|
|
|
441
|
-
const crossingNodes = this.
|
|
534
|
+
const crossingNodes = this.findCrossingRestingLimitOrders(
|
|
442
535
|
marketIndex,
|
|
443
536
|
slot,
|
|
444
537
|
marketType,
|
|
445
538
|
oraclePriceData,
|
|
539
|
+
minAuctionDuration,
|
|
446
540
|
fallbackAsk,
|
|
447
541
|
fallbackBid
|
|
448
542
|
);
|
|
@@ -452,7 +546,7 @@ export class DLOB {
|
|
|
452
546
|
}
|
|
453
547
|
|
|
454
548
|
if (fallbackBid && !isAmmPaused) {
|
|
455
|
-
const askGenerator = this.
|
|
549
|
+
const askGenerator = this.getRestingLimitAsks(
|
|
456
550
|
marketIndex,
|
|
457
551
|
slot,
|
|
458
552
|
marketType,
|
|
@@ -466,7 +560,8 @@ export class DLOB {
|
|
|
466
560
|
fallbackBid,
|
|
467
561
|
(askPrice, fallbackPrice) => {
|
|
468
562
|
return askPrice.lte(fallbackPrice);
|
|
469
|
-
}
|
|
563
|
+
},
|
|
564
|
+
minAuctionDuration
|
|
470
565
|
);
|
|
471
566
|
|
|
472
567
|
for (const askCrossingFallback of asksCrossingFallback) {
|
|
@@ -475,7 +570,7 @@ export class DLOB {
|
|
|
475
570
|
}
|
|
476
571
|
|
|
477
572
|
if (fallbackAsk && !isAmmPaused) {
|
|
478
|
-
const bidGenerator = this.
|
|
573
|
+
const bidGenerator = this.getRestingLimitBids(
|
|
479
574
|
marketIndex,
|
|
480
575
|
slot,
|
|
481
576
|
marketType,
|
|
@@ -489,7 +584,8 @@ export class DLOB {
|
|
|
489
584
|
fallbackAsk,
|
|
490
585
|
(bidPrice, fallbackPrice) => {
|
|
491
586
|
return bidPrice.gte(fallbackPrice);
|
|
492
|
-
}
|
|
587
|
+
},
|
|
588
|
+
minAuctionDuration
|
|
493
589
|
);
|
|
494
590
|
|
|
495
591
|
for (const bidCrossingFallback of bidsCrossingFallback) {
|
|
@@ -500,25 +596,31 @@ export class DLOB {
|
|
|
500
596
|
return nodesToFill;
|
|
501
597
|
}
|
|
502
598
|
|
|
503
|
-
public
|
|
599
|
+
public findTakingNodesToFill(
|
|
504
600
|
marketIndex: number,
|
|
505
601
|
slot: number,
|
|
506
602
|
marketType: MarketType,
|
|
507
603
|
oraclePriceData: OraclePriceData,
|
|
508
604
|
isAmmPaused: boolean,
|
|
605
|
+
minAuctionDuration: number,
|
|
509
606
|
fallbackAsk: BN | undefined,
|
|
510
607
|
fallbackBid?: BN | undefined
|
|
511
608
|
): NodeToFill[] {
|
|
512
609
|
const nodesToFill = new Array<NodeToFill>();
|
|
513
610
|
|
|
514
|
-
let
|
|
611
|
+
let takingOrderGenerator = this.getTakingAsks(
|
|
612
|
+
marketIndex,
|
|
613
|
+
marketType,
|
|
614
|
+
slot,
|
|
615
|
+
oraclePriceData
|
|
616
|
+
);
|
|
515
617
|
|
|
516
|
-
const
|
|
618
|
+
const takingAsksCrossingBids = this.findTakingNodesCrossingMakerNodes(
|
|
517
619
|
marketIndex,
|
|
518
620
|
slot,
|
|
519
621
|
marketType,
|
|
520
622
|
oraclePriceData,
|
|
521
|
-
|
|
623
|
+
takingOrderGenerator,
|
|
522
624
|
this.getMakerLimitBids.bind(this),
|
|
523
625
|
(takerPrice, makerPrice) => {
|
|
524
626
|
if (isVariant(marketType, 'spot')) {
|
|
@@ -534,37 +636,48 @@ export class DLOB {
|
|
|
534
636
|
},
|
|
535
637
|
fallbackAsk
|
|
536
638
|
);
|
|
537
|
-
for (const
|
|
538
|
-
nodesToFill.push(
|
|
639
|
+
for (const takingAskCrossingBid of takingAsksCrossingBids) {
|
|
640
|
+
nodesToFill.push(takingAskCrossingBid);
|
|
539
641
|
}
|
|
540
642
|
|
|
541
643
|
if (fallbackBid && !isAmmPaused) {
|
|
542
|
-
|
|
543
|
-
|
|
644
|
+
takingOrderGenerator = this.getTakingAsks(
|
|
645
|
+
marketIndex,
|
|
646
|
+
marketType,
|
|
647
|
+
slot,
|
|
648
|
+
oraclePriceData
|
|
649
|
+
);
|
|
650
|
+
const takingAsksCrossingFallback =
|
|
544
651
|
this.findNodesCrossingFallbackLiquidity(
|
|
545
652
|
marketType,
|
|
546
653
|
slot,
|
|
547
654
|
oraclePriceData,
|
|
548
|
-
|
|
655
|
+
takingOrderGenerator,
|
|
549
656
|
fallbackBid,
|
|
550
657
|
(takerPrice, fallbackPrice) => {
|
|
551
658
|
return takerPrice === undefined || takerPrice.lte(fallbackPrice);
|
|
552
|
-
}
|
|
659
|
+
},
|
|
660
|
+
minAuctionDuration
|
|
553
661
|
);
|
|
554
662
|
|
|
555
|
-
for (const
|
|
556
|
-
nodesToFill.push(
|
|
663
|
+
for (const takingAskCrossingFallback of takingAsksCrossingFallback) {
|
|
664
|
+
nodesToFill.push(takingAskCrossingFallback);
|
|
557
665
|
}
|
|
558
666
|
}
|
|
559
667
|
|
|
560
|
-
|
|
668
|
+
takingOrderGenerator = this.getTakingBids(
|
|
669
|
+
marketIndex,
|
|
670
|
+
marketType,
|
|
671
|
+
slot,
|
|
672
|
+
oraclePriceData
|
|
673
|
+
);
|
|
561
674
|
|
|
562
|
-
const
|
|
675
|
+
const takingBidsToFill = this.findTakingNodesCrossingMakerNodes(
|
|
563
676
|
marketIndex,
|
|
564
677
|
slot,
|
|
565
678
|
marketType,
|
|
566
679
|
oraclePriceData,
|
|
567
|
-
|
|
680
|
+
takingOrderGenerator,
|
|
568
681
|
this.getMakerLimitAsks.bind(this),
|
|
569
682
|
(takerPrice, makerPrice) => {
|
|
570
683
|
if (isVariant(marketType, 'spot')) {
|
|
@@ -582,24 +695,30 @@ export class DLOB {
|
|
|
582
695
|
fallbackBid
|
|
583
696
|
);
|
|
584
697
|
|
|
585
|
-
for (const
|
|
586
|
-
nodesToFill.push(
|
|
698
|
+
for (const takingBidToFill of takingBidsToFill) {
|
|
699
|
+
nodesToFill.push(takingBidToFill);
|
|
587
700
|
}
|
|
588
701
|
|
|
589
702
|
if (fallbackAsk && !isAmmPaused) {
|
|
590
|
-
|
|
591
|
-
|
|
703
|
+
takingOrderGenerator = this.getTakingBids(
|
|
704
|
+
marketIndex,
|
|
705
|
+
marketType,
|
|
706
|
+
slot,
|
|
707
|
+
oraclePriceData
|
|
708
|
+
);
|
|
709
|
+
const takingBidsCrossingFallback =
|
|
592
710
|
this.findNodesCrossingFallbackLiquidity(
|
|
593
711
|
marketType,
|
|
594
712
|
slot,
|
|
595
713
|
oraclePriceData,
|
|
596
|
-
|
|
714
|
+
takingOrderGenerator,
|
|
597
715
|
fallbackAsk,
|
|
598
716
|
(takerPrice, fallbackPrice) => {
|
|
599
717
|
return takerPrice === undefined || takerPrice.gte(fallbackPrice);
|
|
600
|
-
}
|
|
718
|
+
},
|
|
719
|
+
minAuctionDuration
|
|
601
720
|
);
|
|
602
|
-
for (const marketBidCrossingFallback of
|
|
721
|
+
for (const marketBidCrossingFallback of takingBidsCrossingFallback) {
|
|
603
722
|
nodesToFill.push(marketBidCrossingFallback);
|
|
604
723
|
}
|
|
605
724
|
}
|
|
@@ -607,7 +726,7 @@ export class DLOB {
|
|
|
607
726
|
return nodesToFill;
|
|
608
727
|
}
|
|
609
728
|
|
|
610
|
-
public
|
|
729
|
+
public findTakingNodesCrossingMakerNodes(
|
|
611
730
|
marketIndex: number,
|
|
612
731
|
slot: number,
|
|
613
732
|
marketType: MarketType,
|
|
@@ -671,7 +790,7 @@ export class DLOB {
|
|
|
671
790
|
const newMakerOrder = { ...makerOrder };
|
|
672
791
|
newMakerOrder.baseAssetAmountFilled =
|
|
673
792
|
makerOrder.baseAssetAmountFilled.add(baseFilled);
|
|
674
|
-
this.getListForOrder(newMakerOrder).update(
|
|
793
|
+
this.getListForOrder(newMakerOrder, slot).update(
|
|
675
794
|
newMakerOrder,
|
|
676
795
|
makerNode.userAccount
|
|
677
796
|
);
|
|
@@ -679,7 +798,7 @@ export class DLOB {
|
|
|
679
798
|
const newTakerOrder = { ...takerOrder };
|
|
680
799
|
newTakerOrder.baseAssetAmountFilled =
|
|
681
800
|
takerOrder.baseAssetAmountFilled.add(baseFilled);
|
|
682
|
-
this.getListForOrder(newTakerOrder).update(
|
|
801
|
+
this.getListForOrder(newTakerOrder, slot).update(
|
|
683
802
|
newTakerOrder,
|
|
684
803
|
takerNode.userAccount
|
|
685
804
|
);
|
|
@@ -701,7 +820,8 @@ export class DLOB {
|
|
|
701
820
|
oraclePriceData: OraclePriceData,
|
|
702
821
|
nodeGenerator: Generator<DLOBNode>,
|
|
703
822
|
fallbackPrice: BN,
|
|
704
|
-
doesCross: (nodePrice: BN | undefined, fallbackPrice: BN) => boolean
|
|
823
|
+
doesCross: (nodePrice: BN | undefined, fallbackPrice: BN) => boolean,
|
|
824
|
+
minAuctionDuration: number
|
|
705
825
|
): NodeToFill[] {
|
|
706
826
|
const nodesToFill = new Array<NodeToFill>();
|
|
707
827
|
|
|
@@ -721,7 +841,12 @@ export class DLOB {
|
|
|
721
841
|
|
|
722
842
|
// fallback is available if auction is complete or it's a spot order
|
|
723
843
|
const fallbackAvailable =
|
|
724
|
-
isVariant(marketType, 'spot') ||
|
|
844
|
+
isVariant(marketType, 'spot') ||
|
|
845
|
+
isFallbackAvailableLiquiditySource(
|
|
846
|
+
node.order,
|
|
847
|
+
minAuctionDuration,
|
|
848
|
+
slot
|
|
849
|
+
);
|
|
725
850
|
|
|
726
851
|
if (crosses && fallbackAvailable) {
|
|
727
852
|
nodesToFill.push({
|
|
@@ -752,12 +877,14 @@ export class DLOB {
|
|
|
752
877
|
|
|
753
878
|
// All bids/asks that can expire
|
|
754
879
|
const bidGenerators = [
|
|
755
|
-
nodeLists.
|
|
880
|
+
nodeLists.takingLimit.bid.getGenerator(),
|
|
881
|
+
nodeLists.restingLimit.bid.getGenerator(),
|
|
756
882
|
nodeLists.floatingLimit.bid.getGenerator(),
|
|
757
883
|
nodeLists.market.bid.getGenerator(),
|
|
758
884
|
];
|
|
759
885
|
const askGenerators = [
|
|
760
|
-
nodeLists.
|
|
886
|
+
nodeLists.takingLimit.ask.getGenerator(),
|
|
887
|
+
nodeLists.restingLimit.ask.getGenerator(),
|
|
761
888
|
nodeLists.floatingLimit.ask.getGenerator(),
|
|
762
889
|
nodeLists.market.ask.getGenerator(),
|
|
763
890
|
];
|
|
@@ -788,31 +915,40 @@ export class DLOB {
|
|
|
788
915
|
public findJitAuctionNodesToFill(
|
|
789
916
|
marketIndex: number,
|
|
790
917
|
slot: number,
|
|
918
|
+
oraclePriceData: OraclePriceData,
|
|
791
919
|
marketType: MarketType
|
|
792
920
|
): NodeToFill[] {
|
|
793
921
|
const nodesToFill = new Array<NodeToFill>();
|
|
794
922
|
// Then see if there are orders still in JIT auction
|
|
795
|
-
for (const marketBid of this.
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
923
|
+
for (const marketBid of this.getTakingBids(
|
|
924
|
+
marketIndex,
|
|
925
|
+
marketType,
|
|
926
|
+
slot,
|
|
927
|
+
oraclePriceData
|
|
928
|
+
)) {
|
|
929
|
+
nodesToFill.push({
|
|
930
|
+
node: marketBid,
|
|
931
|
+
});
|
|
801
932
|
}
|
|
802
933
|
|
|
803
|
-
for (const marketAsk of this.
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
934
|
+
for (const marketAsk of this.getTakingAsks(
|
|
935
|
+
marketIndex,
|
|
936
|
+
marketType,
|
|
937
|
+
slot,
|
|
938
|
+
oraclePriceData
|
|
939
|
+
)) {
|
|
940
|
+
nodesToFill.push({
|
|
941
|
+
node: marketAsk,
|
|
942
|
+
});
|
|
809
943
|
}
|
|
810
944
|
return nodesToFill;
|
|
811
945
|
}
|
|
812
946
|
|
|
813
|
-
*
|
|
947
|
+
*getTakingBids(
|
|
814
948
|
marketIndex: number,
|
|
815
|
-
marketType: MarketType
|
|
949
|
+
marketType: MarketType,
|
|
950
|
+
slot: number,
|
|
951
|
+
oraclePriceData: OraclePriceData
|
|
816
952
|
): Generator<DLOBNode> {
|
|
817
953
|
const marketTypeStr = getVariant(marketType) as MarketTypeStr;
|
|
818
954
|
const orderLists = this.orderLists.get(marketTypeStr).get(marketIndex);
|
|
@@ -820,18 +956,28 @@ export class DLOB {
|
|
|
820
956
|
return;
|
|
821
957
|
}
|
|
822
958
|
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
959
|
+
this.updateRestingLimitOrders(slot);
|
|
960
|
+
|
|
961
|
+
const generatorList = [
|
|
962
|
+
orderLists.market.bid.getGenerator(),
|
|
963
|
+
orderLists.takingLimit.bid.getGenerator(),
|
|
964
|
+
];
|
|
965
|
+
|
|
966
|
+
yield* this.getBestNode(
|
|
967
|
+
generatorList,
|
|
968
|
+
oraclePriceData,
|
|
969
|
+
slot,
|
|
970
|
+
(bestNode, currentNode) => {
|
|
971
|
+
return bestNode.order.slot.lt(currentNode.order.slot);
|
|
827
972
|
}
|
|
828
|
-
|
|
829
|
-
}
|
|
973
|
+
);
|
|
830
974
|
}
|
|
831
975
|
|
|
832
|
-
*
|
|
976
|
+
*getTakingAsks(
|
|
833
977
|
marketIndex: number,
|
|
834
|
-
marketType: MarketType
|
|
978
|
+
marketType: MarketType,
|
|
979
|
+
slot: number,
|
|
980
|
+
oraclePriceData: OraclePriceData
|
|
835
981
|
): Generator<DLOBNode> {
|
|
836
982
|
const marketTypeStr = getVariant(marketType) as MarketTypeStr;
|
|
837
983
|
const orderLists = this.orderLists.get(marketTypeStr).get(marketIndex);
|
|
@@ -839,20 +985,33 @@ export class DLOB {
|
|
|
839
985
|
return;
|
|
840
986
|
}
|
|
841
987
|
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
988
|
+
this.updateRestingLimitOrders(slot);
|
|
989
|
+
|
|
990
|
+
const generatorList = [
|
|
991
|
+
orderLists.market.ask.getGenerator(),
|
|
992
|
+
orderLists.takingLimit.ask.getGenerator(),
|
|
993
|
+
];
|
|
994
|
+
|
|
995
|
+
yield* this.getBestNode(
|
|
996
|
+
generatorList,
|
|
997
|
+
oraclePriceData,
|
|
998
|
+
slot,
|
|
999
|
+
(bestNode, currentNode) => {
|
|
1000
|
+
return bestNode.order.slot.lt(currentNode.order.slot);
|
|
846
1001
|
}
|
|
847
|
-
|
|
848
|
-
}
|
|
1002
|
+
);
|
|
849
1003
|
}
|
|
850
1004
|
|
|
851
1005
|
private *getBestNode(
|
|
852
1006
|
generatorList: Array<Generator<DLOBNode>>,
|
|
853
1007
|
oraclePriceData: OraclePriceData,
|
|
854
1008
|
slot: number,
|
|
855
|
-
compareFcn: (
|
|
1009
|
+
compareFcn: (
|
|
1010
|
+
bestDLOBNode: DLOBNode,
|
|
1011
|
+
currentDLOBNode: DLOBNode,
|
|
1012
|
+
slot: number,
|
|
1013
|
+
oraclePriceData: OraclePriceData
|
|
1014
|
+
) => boolean
|
|
856
1015
|
): Generator<DLOBNode> {
|
|
857
1016
|
const generators = generatorList.map((generator) => {
|
|
858
1017
|
return {
|
|
@@ -876,18 +1035,7 @@ export class DLOB {
|
|
|
876
1035
|
const bestValue = bestGenerator.next.value as DLOBNode;
|
|
877
1036
|
const currentValue = currentGenerator.next.value as DLOBNode;
|
|
878
1037
|
|
|
879
|
-
|
|
880
|
-
if (bestValue.order && isMarketOrder(bestValue.order)) {
|
|
881
|
-
return bestGenerator;
|
|
882
|
-
}
|
|
883
|
-
if (currentValue.order && isMarketOrder(currentValue.order)) {
|
|
884
|
-
return currentGenerator;
|
|
885
|
-
}
|
|
886
|
-
|
|
887
|
-
const bestPrice = bestValue.getPrice(oraclePriceData, slot);
|
|
888
|
-
const currentPrice = currentValue.getPrice(oraclePriceData, slot);
|
|
889
|
-
|
|
890
|
-
return compareFcn(bestPrice, currentPrice)
|
|
1038
|
+
return compareFcn(bestValue, currentValue, slot, oraclePriceData)
|
|
891
1039
|
? bestGenerator
|
|
892
1040
|
: currentGenerator;
|
|
893
1041
|
}
|
|
@@ -908,7 +1056,7 @@ export class DLOB {
|
|
|
908
1056
|
}
|
|
909
1057
|
}
|
|
910
1058
|
|
|
911
|
-
*
|
|
1059
|
+
*getRestingLimitAsks(
|
|
912
1060
|
marketIndex: number,
|
|
913
1061
|
slot: number,
|
|
914
1062
|
marketType: MarketType,
|
|
@@ -917,6 +1065,9 @@ export class DLOB {
|
|
|
917
1065
|
if (isVariant(marketType, 'spot') && !oraclePriceData) {
|
|
918
1066
|
throw new Error('Must provide OraclePriceData to get spot asks');
|
|
919
1067
|
}
|
|
1068
|
+
|
|
1069
|
+
this.updateRestingLimitOrders(slot);
|
|
1070
|
+
|
|
920
1071
|
const marketTypeStr = getVariant(marketType) as MarketTypeStr;
|
|
921
1072
|
const nodeLists = this.orderLists.get(marketTypeStr).get(marketIndex);
|
|
922
1073
|
|
|
@@ -925,7 +1076,7 @@ export class DLOB {
|
|
|
925
1076
|
}
|
|
926
1077
|
|
|
927
1078
|
const generatorList = [
|
|
928
|
-
nodeLists.
|
|
1079
|
+
nodeLists.restingLimit.ask.getGenerator(),
|
|
929
1080
|
nodeLists.floatingLimit.ask.getGenerator(),
|
|
930
1081
|
];
|
|
931
1082
|
|
|
@@ -933,15 +1084,17 @@ export class DLOB {
|
|
|
933
1084
|
generatorList,
|
|
934
1085
|
oraclePriceData,
|
|
935
1086
|
slot,
|
|
936
|
-
(
|
|
937
|
-
return
|
|
1087
|
+
(bestNode, currentNode, slot, oraclePriceData) => {
|
|
1088
|
+
return bestNode
|
|
1089
|
+
.getPrice(oraclePriceData, slot)
|
|
1090
|
+
.lt(currentNode.getPrice(oraclePriceData, slot));
|
|
938
1091
|
}
|
|
939
1092
|
);
|
|
940
1093
|
}
|
|
941
1094
|
|
|
942
1095
|
/**
|
|
943
|
-
* Filters the limit asks that are
|
|
944
|
-
*
|
|
1096
|
+
* Filters the limit asks that are resting and do not cross fallback bid
|
|
1097
|
+
* Taking orders can only fill against orders that meet this criteria
|
|
945
1098
|
*
|
|
946
1099
|
* @returns
|
|
947
1100
|
*/
|
|
@@ -952,28 +1105,23 @@ export class DLOB {
|
|
|
952
1105
|
oraclePriceData: OraclePriceData,
|
|
953
1106
|
fallbackBid?: BN
|
|
954
1107
|
): Generator<DLOBNode> {
|
|
955
|
-
for (const node of this.
|
|
1108
|
+
for (const node of this.getRestingLimitAsks(
|
|
956
1109
|
marketIndex,
|
|
957
1110
|
slot,
|
|
958
1111
|
marketType,
|
|
959
1112
|
oraclePriceData
|
|
960
1113
|
)) {
|
|
961
|
-
if (
|
|
962
|
-
yield node;
|
|
963
|
-
} else if (
|
|
1114
|
+
if (
|
|
964
1115
|
fallbackBid &&
|
|
965
|
-
node.getPrice(oraclePriceData, slot).
|
|
1116
|
+
node.getPrice(oraclePriceData, slot).lte(fallbackBid)
|
|
966
1117
|
) {
|
|
967
|
-
|
|
1118
|
+
continue;
|
|
968
1119
|
}
|
|
1120
|
+
yield node;
|
|
969
1121
|
}
|
|
970
1122
|
}
|
|
971
1123
|
|
|
972
|
-
|
|
973
|
-
return order.postOnly || new BN(slot).sub(order.slot).gte(new BN(45));
|
|
974
|
-
}
|
|
975
|
-
|
|
976
|
-
*getLimitBids(
|
|
1124
|
+
*getRestingLimitBids(
|
|
977
1125
|
marketIndex: number,
|
|
978
1126
|
slot: number,
|
|
979
1127
|
marketType: MarketType,
|
|
@@ -983,6 +1131,8 @@ export class DLOB {
|
|
|
983
1131
|
throw new Error('Must provide OraclePriceData to get spot bids');
|
|
984
1132
|
}
|
|
985
1133
|
|
|
1134
|
+
this.updateRestingLimitOrders(slot);
|
|
1135
|
+
|
|
986
1136
|
const marketTypeStr = getVariant(marketType) as MarketTypeStr;
|
|
987
1137
|
const nodeLists = this.orderLists.get(marketTypeStr).get(marketIndex);
|
|
988
1138
|
|
|
@@ -991,7 +1141,7 @@ export class DLOB {
|
|
|
991
1141
|
}
|
|
992
1142
|
|
|
993
1143
|
const generatorList = [
|
|
994
|
-
nodeLists.
|
|
1144
|
+
nodeLists.restingLimit.bid.getGenerator(),
|
|
995
1145
|
nodeLists.floatingLimit.bid.getGenerator(),
|
|
996
1146
|
];
|
|
997
1147
|
|
|
@@ -999,8 +1149,10 @@ export class DLOB {
|
|
|
999
1149
|
generatorList,
|
|
1000
1150
|
oraclePriceData,
|
|
1001
1151
|
slot,
|
|
1002
|
-
(
|
|
1003
|
-
return
|
|
1152
|
+
(bestNode, currentNode, slot, oraclePriceData) => {
|
|
1153
|
+
return bestNode
|
|
1154
|
+
.getPrice(oraclePriceData, slot)
|
|
1155
|
+
.gt(currentNode.getPrice(oraclePriceData, slot));
|
|
1004
1156
|
}
|
|
1005
1157
|
);
|
|
1006
1158
|
}
|
|
@@ -1018,20 +1170,19 @@ export class DLOB {
|
|
|
1018
1170
|
oraclePriceData: OraclePriceData,
|
|
1019
1171
|
fallbackAsk?: BN
|
|
1020
1172
|
): Generator<DLOBNode> {
|
|
1021
|
-
for (const node of this.
|
|
1173
|
+
for (const node of this.getRestingLimitBids(
|
|
1022
1174
|
marketIndex,
|
|
1023
1175
|
slot,
|
|
1024
1176
|
marketType,
|
|
1025
1177
|
oraclePriceData
|
|
1026
1178
|
)) {
|
|
1027
|
-
if (
|
|
1028
|
-
yield node;
|
|
1029
|
-
} else if (
|
|
1179
|
+
if (
|
|
1030
1180
|
fallbackAsk &&
|
|
1031
|
-
node.getPrice(oraclePriceData, slot).
|
|
1181
|
+
node.getPrice(oraclePriceData, slot).gte(fallbackAsk)
|
|
1032
1182
|
) {
|
|
1033
|
-
|
|
1183
|
+
continue;
|
|
1034
1184
|
}
|
|
1185
|
+
yield node;
|
|
1035
1186
|
}
|
|
1036
1187
|
}
|
|
1037
1188
|
|
|
@@ -1047,8 +1198,8 @@ export class DLOB {
|
|
|
1047
1198
|
}
|
|
1048
1199
|
|
|
1049
1200
|
const generatorList = [
|
|
1050
|
-
this.
|
|
1051
|
-
this.
|
|
1201
|
+
this.getTakingAsks(marketIndex, marketType, slot, oraclePriceData),
|
|
1202
|
+
this.getRestingLimitAsks(marketIndex, slot, marketType, oraclePriceData),
|
|
1052
1203
|
];
|
|
1053
1204
|
|
|
1054
1205
|
const marketTypeStr = getVariant(marketType) as MarketTypeStr;
|
|
@@ -1060,8 +1211,29 @@ export class DLOB {
|
|
|
1060
1211
|
generatorList,
|
|
1061
1212
|
oraclePriceData,
|
|
1062
1213
|
slot,
|
|
1063
|
-
(
|
|
1064
|
-
|
|
1214
|
+
(bestNode, currentNode, slot, oraclePriceData) => {
|
|
1215
|
+
const bestNodeTaking = bestNode.order
|
|
1216
|
+
? isTakingOrder(bestNode.order, slot)
|
|
1217
|
+
: false;
|
|
1218
|
+
const currentNodeTaking = currentNode.order
|
|
1219
|
+
? isTakingOrder(currentNode.order, slot)
|
|
1220
|
+
: false;
|
|
1221
|
+
|
|
1222
|
+
if (bestNodeTaking && currentNodeTaking) {
|
|
1223
|
+
return bestNode.order.slot.lt(currentNode.order.slot);
|
|
1224
|
+
}
|
|
1225
|
+
|
|
1226
|
+
if (bestNodeTaking) {
|
|
1227
|
+
return true;
|
|
1228
|
+
}
|
|
1229
|
+
|
|
1230
|
+
if (currentNodeTaking) {
|
|
1231
|
+
return false;
|
|
1232
|
+
}
|
|
1233
|
+
|
|
1234
|
+
return bestNode
|
|
1235
|
+
.getPrice(oraclePriceData, slot)
|
|
1236
|
+
.lt(currentNode.getPrice(oraclePriceData, slot));
|
|
1065
1237
|
}
|
|
1066
1238
|
);
|
|
1067
1239
|
}
|
|
@@ -1078,8 +1250,8 @@ export class DLOB {
|
|
|
1078
1250
|
}
|
|
1079
1251
|
|
|
1080
1252
|
const generatorList = [
|
|
1081
|
-
this.
|
|
1082
|
-
this.
|
|
1253
|
+
this.getTakingBids(marketIndex, marketType, slot, oraclePriceData),
|
|
1254
|
+
this.getRestingLimitBids(marketIndex, slot, marketType, oraclePriceData),
|
|
1083
1255
|
];
|
|
1084
1256
|
|
|
1085
1257
|
const marketTypeStr = getVariant(marketType) as MarketTypeStr;
|
|
@@ -1091,40 +1263,64 @@ export class DLOB {
|
|
|
1091
1263
|
generatorList,
|
|
1092
1264
|
oraclePriceData,
|
|
1093
1265
|
slot,
|
|
1094
|
-
(
|
|
1095
|
-
|
|
1266
|
+
(bestNode, currentNode, slot, oraclePriceData) => {
|
|
1267
|
+
const bestNodeTaking = bestNode.order
|
|
1268
|
+
? isTakingOrder(bestNode.order, slot)
|
|
1269
|
+
: false;
|
|
1270
|
+
const currentNodeTaking = currentNode.order
|
|
1271
|
+
? isTakingOrder(currentNode.order, slot)
|
|
1272
|
+
: false;
|
|
1273
|
+
|
|
1274
|
+
if (bestNodeTaking && currentNodeTaking) {
|
|
1275
|
+
return bestNode.order.slot.lt(currentNode.order.slot);
|
|
1276
|
+
}
|
|
1277
|
+
|
|
1278
|
+
if (bestNodeTaking) {
|
|
1279
|
+
return true;
|
|
1280
|
+
}
|
|
1281
|
+
|
|
1282
|
+
if (currentNodeTaking) {
|
|
1283
|
+
return false;
|
|
1284
|
+
}
|
|
1285
|
+
|
|
1286
|
+
return bestNode
|
|
1287
|
+
.getPrice(oraclePriceData, slot)
|
|
1288
|
+
.gt(currentNode.getPrice(oraclePriceData, slot));
|
|
1096
1289
|
}
|
|
1097
1290
|
);
|
|
1098
1291
|
}
|
|
1099
1292
|
|
|
1100
|
-
|
|
1293
|
+
findCrossingRestingLimitOrders(
|
|
1101
1294
|
marketIndex: number,
|
|
1102
1295
|
slot: number,
|
|
1103
1296
|
marketType: MarketType,
|
|
1104
1297
|
oraclePriceData: OraclePriceData,
|
|
1298
|
+
minAuctionDuration: number,
|
|
1105
1299
|
fallbackAsk: BN | undefined,
|
|
1106
1300
|
fallbackBid: BN | undefined
|
|
1107
1301
|
): NodeToFill[] {
|
|
1108
1302
|
const nodesToFill = new Array<NodeToFill>();
|
|
1109
1303
|
|
|
1110
|
-
for (const askNode of this.
|
|
1304
|
+
for (const askNode of this.getRestingLimitAsks(
|
|
1111
1305
|
marketIndex,
|
|
1112
1306
|
slot,
|
|
1113
1307
|
marketType,
|
|
1114
1308
|
oraclePriceData
|
|
1115
1309
|
)) {
|
|
1116
|
-
|
|
1310
|
+
const bidGenerator = this.getRestingLimitBids(
|
|
1117
1311
|
marketIndex,
|
|
1118
1312
|
slot,
|
|
1119
1313
|
marketType,
|
|
1120
1314
|
oraclePriceData
|
|
1121
|
-
)
|
|
1315
|
+
);
|
|
1316
|
+
|
|
1317
|
+
for (const bidNode of bidGenerator) {
|
|
1122
1318
|
const bidPrice = bidNode.getPrice(oraclePriceData, slot);
|
|
1123
1319
|
const askPrice = askNode.getPrice(oraclePriceData, slot);
|
|
1124
1320
|
|
|
1125
|
-
// orders don't cross
|
|
1321
|
+
// orders don't cross
|
|
1126
1322
|
if (bidPrice.lt(askPrice)) {
|
|
1127
|
-
|
|
1323
|
+
break;
|
|
1128
1324
|
}
|
|
1129
1325
|
|
|
1130
1326
|
const bidOrder = bidNode.order;
|
|
@@ -1132,17 +1328,27 @@ export class DLOB {
|
|
|
1132
1328
|
|
|
1133
1329
|
// Can't match orders from the same user
|
|
1134
1330
|
const sameUser = bidNode.userAccount.equals(askNode.userAccount);
|
|
1135
|
-
if (sameUser
|
|
1331
|
+
if (sameUser) {
|
|
1136
1332
|
continue;
|
|
1137
1333
|
}
|
|
1138
1334
|
|
|
1139
|
-
const
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
)
|
|
1335
|
+
const makerAndTaker = this.determineMakerAndTaker(askNode, bidNode);
|
|
1336
|
+
|
|
1337
|
+
// unable to match maker and taker due to post only or slot
|
|
1338
|
+
if (!makerAndTaker) {
|
|
1339
|
+
continue;
|
|
1340
|
+
}
|
|
1341
|
+
|
|
1342
|
+
const { takerNode, makerNode } = makerAndTaker;
|
|
1143
1343
|
|
|
1144
1344
|
// extra guard against bad fills for limit orders where auction is incomplete
|
|
1145
|
-
if (
|
|
1345
|
+
if (
|
|
1346
|
+
!isFallbackAvailableLiquiditySource(
|
|
1347
|
+
takerNode.order,
|
|
1348
|
+
minAuctionDuration,
|
|
1349
|
+
slot
|
|
1350
|
+
)
|
|
1351
|
+
) {
|
|
1146
1352
|
let bidPrice: BN;
|
|
1147
1353
|
let askPrice: BN;
|
|
1148
1354
|
if (isVariant(takerNode.order.direction, 'long')) {
|
|
@@ -1176,7 +1382,7 @@ export class DLOB {
|
|
|
1176
1382
|
const newBidOrder = { ...bidOrder };
|
|
1177
1383
|
newBidOrder.baseAssetAmountFilled =
|
|
1178
1384
|
bidOrder.baseAssetAmountFilled.add(baseFilled);
|
|
1179
|
-
this.getListForOrder(newBidOrder).update(
|
|
1385
|
+
this.getListForOrder(newBidOrder, slot).update(
|
|
1180
1386
|
newBidOrder,
|
|
1181
1387
|
bidNode.userAccount
|
|
1182
1388
|
);
|
|
@@ -1185,7 +1391,7 @@ export class DLOB {
|
|
|
1185
1391
|
const newAskOrder = { ...askOrder };
|
|
1186
1392
|
newAskOrder.baseAssetAmountFilled =
|
|
1187
1393
|
askOrder.baseAssetAmountFilled.add(baseFilled);
|
|
1188
|
-
this.getListForOrder(newAskOrder).update(
|
|
1394
|
+
this.getListForOrder(newAskOrder, slot).update(
|
|
1189
1395
|
newAskOrder,
|
|
1190
1396
|
askNode.userAccount
|
|
1191
1397
|
);
|
|
@@ -1207,27 +1413,25 @@ export class DLOB {
|
|
|
1207
1413
|
determineMakerAndTaker(
|
|
1208
1414
|
askNode: DLOBNode,
|
|
1209
1415
|
bidNode: DLOBNode
|
|
1210
|
-
): { takerNode: DLOBNode; makerNode: DLOBNode } {
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
takerNode: bidNode,
|
|
1219
|
-
makerNode: askNode,
|
|
1220
|
-
};
|
|
1221
|
-
} else if (askNode.order.slot.lt(bidNode.order.slot)) {
|
|
1416
|
+
): { takerNode: DLOBNode; makerNode: DLOBNode } | undefined {
|
|
1417
|
+
const askSlot = askNode.order.slot.add(
|
|
1418
|
+
new BN(askNode.order.auctionDuration)
|
|
1419
|
+
);
|
|
1420
|
+
const bidSlot = bidNode.order.slot.add(
|
|
1421
|
+
new BN(bidNode.order.auctionDuration)
|
|
1422
|
+
);
|
|
1423
|
+
if (askSlot.lte(bidSlot) && !bidNode.order.postOnly) {
|
|
1222
1424
|
return {
|
|
1223
1425
|
takerNode: bidNode,
|
|
1224
1426
|
makerNode: askNode,
|
|
1225
1427
|
};
|
|
1226
|
-
} else {
|
|
1428
|
+
} else if (bidSlot.lte(askSlot) && !askNode.order.postOnly) {
|
|
1227
1429
|
return {
|
|
1228
1430
|
takerNode: askNode,
|
|
1229
1431
|
makerNode: bidNode,
|
|
1230
1432
|
};
|
|
1433
|
+
} else {
|
|
1434
|
+
return undefined;
|
|
1231
1435
|
}
|
|
1232
1436
|
}
|
|
1233
1437
|
|
|
@@ -1288,11 +1492,9 @@ export class DLOB {
|
|
|
1288
1492
|
if (triggerAboveList) {
|
|
1289
1493
|
for (const node of triggerAboveList.getGenerator()) {
|
|
1290
1494
|
if (oraclePrice.gt(node.order.triggerPrice)) {
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
});
|
|
1295
|
-
}
|
|
1495
|
+
nodesToTrigger.push({
|
|
1496
|
+
node: node,
|
|
1497
|
+
});
|
|
1296
1498
|
} else {
|
|
1297
1499
|
break;
|
|
1298
1500
|
}
|
|
@@ -1305,11 +1507,9 @@ export class DLOB {
|
|
|
1305
1507
|
if (triggerBelowList) {
|
|
1306
1508
|
for (const node of triggerBelowList.getGenerator()) {
|
|
1307
1509
|
if (oraclePrice.lt(node.order.triggerPrice)) {
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
});
|
|
1312
|
-
}
|
|
1510
|
+
nodesToTrigger.push({
|
|
1511
|
+
node: node,
|
|
1512
|
+
});
|
|
1313
1513
|
} else {
|
|
1314
1514
|
break;
|
|
1315
1515
|
}
|
|
@@ -1438,8 +1638,10 @@ export class DLOB {
|
|
|
1438
1638
|
|
|
1439
1639
|
*getNodeLists(): Generator<NodeList<DLOBNodeType>> {
|
|
1440
1640
|
for (const [_, nodeLists] of this.orderLists.get('perp')) {
|
|
1441
|
-
yield nodeLists.
|
|
1442
|
-
yield nodeLists.
|
|
1641
|
+
yield nodeLists.restingLimit.bid;
|
|
1642
|
+
yield nodeLists.restingLimit.ask;
|
|
1643
|
+
yield nodeLists.takingLimit.bid;
|
|
1644
|
+
yield nodeLists.takingLimit.ask;
|
|
1443
1645
|
yield nodeLists.market.bid;
|
|
1444
1646
|
yield nodeLists.market.ask;
|
|
1445
1647
|
yield nodeLists.floatingLimit.bid;
|
|
@@ -1449,8 +1651,10 @@ export class DLOB {
|
|
|
1449
1651
|
}
|
|
1450
1652
|
|
|
1451
1653
|
for (const [_, nodeLists] of this.orderLists.get('spot')) {
|
|
1452
|
-
yield nodeLists.
|
|
1453
|
-
yield nodeLists.
|
|
1654
|
+
yield nodeLists.restingLimit.bid;
|
|
1655
|
+
yield nodeLists.restingLimit.ask;
|
|
1656
|
+
yield nodeLists.takingLimit.bid;
|
|
1657
|
+
yield nodeLists.takingLimit.ask;
|
|
1454
1658
|
yield nodeLists.market.bid;
|
|
1455
1659
|
yield nodeLists.market.ask;
|
|
1456
1660
|
yield nodeLists.floatingLimit.bid;
|