@drift-labs/sdk 2.74.0-beta.7 → 2.74.0-beta.8
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/VERSION +1 -1
- package/lib/dlob/orderBookLevels.js +41 -12
- package/package.json +1 -1
- package/src/dlob/orderBookLevels.ts +45 -15
- package/tests/dlob/test.ts +57 -0
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
2.74.0-beta.
|
|
1
|
+
2.74.0-beta.8
|
|
@@ -261,6 +261,31 @@ function groupL2Levels(levels, grouping, direction, depth) {
|
|
|
261
261
|
}
|
|
262
262
|
return groupedLevels;
|
|
263
263
|
}
|
|
264
|
+
/**
|
|
265
|
+
* Method to merge bids or asks by price
|
|
266
|
+
*/
|
|
267
|
+
const mergeByPrice = (bidsOrAsks) => {
|
|
268
|
+
const merged = new Map();
|
|
269
|
+
for (const level of bidsOrAsks) {
|
|
270
|
+
const key = level.price.toString();
|
|
271
|
+
if (merged.has(key)) {
|
|
272
|
+
const existing = merged.get(key);
|
|
273
|
+
existing.size = existing.size.add(level.size);
|
|
274
|
+
for (const [source, size] of Object.entries(level.sources)) {
|
|
275
|
+
if (existing.sources[source]) {
|
|
276
|
+
existing.sources[source] = existing.sources[source].add(size);
|
|
277
|
+
}
|
|
278
|
+
else {
|
|
279
|
+
existing.sources[source] = size;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
else {
|
|
284
|
+
merged.set(key, cloneL2Level(level));
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
return Array.from(merged.values());
|
|
288
|
+
};
|
|
264
289
|
/**
|
|
265
290
|
* The purpose of this function is uncross the L2 orderbook by modifying the bid/ask price at the top of the book
|
|
266
291
|
* This will make the liquidity look worse but more intuitive (users familiar with clob get confused w temporarily
|
|
@@ -342,17 +367,17 @@ function uncrossL2(bids, asks, oraclePrice, oracleTwap5Min, markTwap5Min, groupi
|
|
|
342
367
|
bidIndex++;
|
|
343
368
|
continue;
|
|
344
369
|
}
|
|
370
|
+
if (userBids.has(nextBid.price.toString())) {
|
|
371
|
+
newBids.push(nextBid);
|
|
372
|
+
bidIndex++;
|
|
373
|
+
continue;
|
|
374
|
+
}
|
|
375
|
+
if (userAsks.has(nextAsk.price.toString())) {
|
|
376
|
+
newAsks.push(nextAsk);
|
|
377
|
+
askIndex++;
|
|
378
|
+
continue;
|
|
379
|
+
}
|
|
345
380
|
if (nextBid.price.gte(nextAsk.price)) {
|
|
346
|
-
if (userBids.has(nextBid.price.toString())) {
|
|
347
|
-
newBids.push(nextBid);
|
|
348
|
-
bidIndex++;
|
|
349
|
-
continue;
|
|
350
|
-
}
|
|
351
|
-
if (userAsks.has(nextAsk.price.toString())) {
|
|
352
|
-
newAsks.push(nextAsk);
|
|
353
|
-
askIndex++;
|
|
354
|
-
continue;
|
|
355
|
-
}
|
|
356
381
|
if (nextBid.price.gt(referencePrice) &&
|
|
357
382
|
nextAsk.price.gt(referencePrice)) {
|
|
358
383
|
let newBidPrice = nextAsk.price.sub(grouping);
|
|
@@ -397,9 +422,13 @@ function uncrossL2(bids, asks, oraclePrice, oracleTwap5Min, markTwap5Min, groupi
|
|
|
397
422
|
bidIndex++;
|
|
398
423
|
}
|
|
399
424
|
}
|
|
425
|
+
newBids.sort((a, b) => b.price.cmp(a.price));
|
|
426
|
+
newAsks.sort((a, b) => a.price.cmp(b.price));
|
|
427
|
+
const finalNewBids = mergeByPrice(newBids);
|
|
428
|
+
const finalNewAsks = mergeByPrice(newAsks);
|
|
400
429
|
return {
|
|
401
|
-
bids:
|
|
402
|
-
asks:
|
|
430
|
+
bids: finalNewBids,
|
|
431
|
+
asks: finalNewAsks,
|
|
403
432
|
};
|
|
404
433
|
}
|
|
405
434
|
exports.uncrossL2 = uncrossL2;
|
package/package.json
CHANGED
|
@@ -429,6 +429,30 @@ function groupL2Levels(
|
|
|
429
429
|
return groupedLevels;
|
|
430
430
|
}
|
|
431
431
|
|
|
432
|
+
/**
|
|
433
|
+
* Method to merge bids or asks by price
|
|
434
|
+
*/
|
|
435
|
+
const mergeByPrice = (bidsOrAsks: L2Level[]) => {
|
|
436
|
+
const merged = new Map<string, L2Level>();
|
|
437
|
+
for (const level of bidsOrAsks) {
|
|
438
|
+
const key = level.price.toString();
|
|
439
|
+
if (merged.has(key)) {
|
|
440
|
+
const existing = merged.get(key);
|
|
441
|
+
existing.size = existing.size.add(level.size);
|
|
442
|
+
for (const [source, size] of Object.entries(level.sources)) {
|
|
443
|
+
if (existing.sources[source]) {
|
|
444
|
+
existing.sources[source] = existing.sources[source].add(size);
|
|
445
|
+
} else {
|
|
446
|
+
existing.sources[source] = size;
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
} else {
|
|
450
|
+
merged.set(key, cloneL2Level(level));
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
return Array.from(merged.values());
|
|
454
|
+
};
|
|
455
|
+
|
|
432
456
|
/**
|
|
433
457
|
* The purpose of this function is uncross the L2 orderbook by modifying the bid/ask price at the top of the book
|
|
434
458
|
* This will make the liquidity look worse but more intuitive (users familiar with clob get confused w temporarily
|
|
@@ -467,8 +491,8 @@ export function uncrossL2(
|
|
|
467
491
|
return { bids, asks };
|
|
468
492
|
}
|
|
469
493
|
|
|
470
|
-
const newBids = [];
|
|
471
|
-
const newAsks = [];
|
|
494
|
+
const newBids: L2Level[] = [];
|
|
495
|
+
const newAsks: L2Level[] = [];
|
|
472
496
|
|
|
473
497
|
const updateLevels = (newPrice: BN, oldLevel: L2Level, levels: L2Level[]) => {
|
|
474
498
|
if (levels.length > 0 && levels[levels.length - 1].price.eq(newPrice)) {
|
|
@@ -528,19 +552,19 @@ export function uncrossL2(
|
|
|
528
552
|
continue;
|
|
529
553
|
}
|
|
530
554
|
|
|
531
|
-
if (nextBid.price.
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
}
|
|
555
|
+
if (userBids.has(nextBid.price.toString())) {
|
|
556
|
+
newBids.push(nextBid);
|
|
557
|
+
bidIndex++;
|
|
558
|
+
continue;
|
|
559
|
+
}
|
|
537
560
|
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
561
|
+
if (userAsks.has(nextAsk.price.toString())) {
|
|
562
|
+
newAsks.push(nextAsk);
|
|
563
|
+
askIndex++;
|
|
564
|
+
continue;
|
|
565
|
+
}
|
|
543
566
|
|
|
567
|
+
if (nextBid.price.gte(nextAsk.price)) {
|
|
544
568
|
if (
|
|
545
569
|
nextBid.price.gt(referencePrice) &&
|
|
546
570
|
nextAsk.price.gt(referencePrice)
|
|
@@ -594,8 +618,14 @@ export function uncrossL2(
|
|
|
594
618
|
}
|
|
595
619
|
}
|
|
596
620
|
|
|
621
|
+
newBids.sort((a, b) => b.price.cmp(a.price));
|
|
622
|
+
newAsks.sort((a, b) => a.price.cmp(b.price));
|
|
623
|
+
|
|
624
|
+
const finalNewBids = mergeByPrice(newBids);
|
|
625
|
+
const finalNewAsks = mergeByPrice(newAsks);
|
|
626
|
+
|
|
597
627
|
return {
|
|
598
|
-
bids:
|
|
599
|
-
asks:
|
|
628
|
+
bids: finalNewBids,
|
|
629
|
+
asks: finalNewAsks,
|
|
600
630
|
};
|
|
601
631
|
}
|
package/tests/dlob/test.ts
CHANGED
|
@@ -6614,6 +6614,63 @@ describe('Uncross L2', () => {
|
|
|
6614
6614
|
);
|
|
6615
6615
|
});
|
|
6616
6616
|
|
|
6617
|
+
it('Handles user crossing bid in second level', () => {
|
|
6618
|
+
const oraclePrice = new BN(190.3843 * PRICE_PRECISION.toNumber());
|
|
6619
|
+
const bids = [
|
|
6620
|
+
[190.59, 2],
|
|
6621
|
+
[190.588, 58.3],
|
|
6622
|
+
[190.5557, 5],
|
|
6623
|
+
[190.5547, 5],
|
|
6624
|
+
[190.5508, 5],
|
|
6625
|
+
[190.541, 2],
|
|
6626
|
+
[190.5099, 49.1],
|
|
6627
|
+
[190.5, 60],
|
|
6628
|
+
].map(([price, size]) => ({
|
|
6629
|
+
price: new BN(price * PRICE_PRECISION.toNumber()),
|
|
6630
|
+
size: new BN(size * BASE_PRECISION.toNumber()),
|
|
6631
|
+
sources: { vamm: new BN(size * BASE_PRECISION.toNumber()) },
|
|
6632
|
+
}));
|
|
6633
|
+
|
|
6634
|
+
const asks = [
|
|
6635
|
+
[190.5, 86.5],
|
|
6636
|
+
[190.6159, 1],
|
|
6637
|
+
[190.656, 10.5],
|
|
6638
|
+
[190.6561, 1],
|
|
6639
|
+
[190.6585, 5],
|
|
6640
|
+
[190.6595, 5],
|
|
6641
|
+
[190.6596, 5],
|
|
6642
|
+
].map(([price, size]) => ({
|
|
6643
|
+
price: new BN(price * PRICE_PRECISION.toNumber()),
|
|
6644
|
+
size: new BN(size * BASE_PRECISION.toNumber()),
|
|
6645
|
+
sources: { vamm: new BN(size * BASE_PRECISION.toNumber()) },
|
|
6646
|
+
}));
|
|
6647
|
+
|
|
6648
|
+
expect(asksAreSortedAsc(asks), 'Input asks are ascending').to.be.true;
|
|
6649
|
+
expect(bidsAreSortedDesc(bids), 'Input bids are descending').to.be.true;
|
|
6650
|
+
|
|
6651
|
+
const groupingSize = new BN('100');
|
|
6652
|
+
|
|
6653
|
+
const userBidPrice = new BN(190.588 * PRICE_PRECISION.toNumber());
|
|
6654
|
+
const userBids = new Set<string>([userBidPrice.toString()]);
|
|
6655
|
+
|
|
6656
|
+
const { bids: newBids, asks: newAsks } = uncrossL2(
|
|
6657
|
+
bids,
|
|
6658
|
+
asks,
|
|
6659
|
+
oraclePrice,
|
|
6660
|
+
oraclePrice,
|
|
6661
|
+
oraclePrice,
|
|
6662
|
+
groupingSize,
|
|
6663
|
+
userBids,
|
|
6664
|
+
new Set<string>()
|
|
6665
|
+
);
|
|
6666
|
+
|
|
6667
|
+
expect(asksAreSortedAsc(newAsks), 'Uncrossed asks are ascending').to.be
|
|
6668
|
+
.true;
|
|
6669
|
+
expect(bidsAreSortedDesc(newBids), 'Uncrossed bids are descending').to.be
|
|
6670
|
+
.true;
|
|
6671
|
+
expect(newBids[0].price.toString()).to.equal(userBidPrice.toString());
|
|
6672
|
+
});
|
|
6673
|
+
|
|
6617
6674
|
it('Handles edge case bide and asks with large cross and an overlapping level', () => {
|
|
6618
6675
|
const bids = [
|
|
6619
6676
|
'104411000',
|