@drift-labs/sdk 2.0.14 → 2.0.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/lib/accounts/pollingDriftClientAccountSubscriber.js +0 -3
- package/lib/accounts/pollingUserStatsAccountSubscriber.js +0 -3
- package/lib/adminClient.d.ts +2 -0
- package/lib/adminClient.js +16 -0
- package/lib/constants/numericConstants.d.ts +3 -0
- package/lib/constants/numericConstants.js +4 -1
- package/lib/dlob/DLOB.d.ts +2 -7
- package/lib/dlob/DLOB.js +84 -166
- package/lib/idl/drift.json +65 -7
- package/lib/math/amm.d.ts +8 -7
- package/lib/math/amm.js +116 -61
- package/lib/math/oracles.d.ts +2 -0
- package/lib/math/oracles.js +23 -1
- package/lib/math/utils.d.ts +2 -1
- package/lib/math/utils.js +5 -1
- package/lib/types.d.ts +6 -2
- package/lib/user.js +10 -6
- package/package.json +1 -1
- package/src/accounts/pollingDriftClientAccountSubscriber.ts +0 -4
- package/src/accounts/pollingUserStatsAccountSubscriber.ts +0 -4
- package/src/adminClient.ts +28 -0
- package/src/constants/numericConstants.ts +7 -0
- package/src/dlob/DLOB.ts +152 -237
- package/src/idl/drift.json +65 -7
- package/src/math/amm.ts +220 -100
- package/src/math/oracles.ts +52 -0
- package/src/math/utils.ts +5 -1
- package/src/types.ts +6 -2
- package/src/user.ts +17 -6
- package/tests/dlob/helpers.ts +4 -2
- package/tests/dlob/test.ts +252 -0
- package/src/events/eventList.js +0 -77
package/src/math/amm.ts
CHANGED
|
@@ -10,6 +10,9 @@ import {
|
|
|
10
10
|
QUOTE_PRECISION,
|
|
11
11
|
MARGIN_PRECISION,
|
|
12
12
|
PRICE_DIV_PEG,
|
|
13
|
+
PERCENTAGE_PRECISION,
|
|
14
|
+
BASE_PRECISION,
|
|
15
|
+
DEFAULT_REVENUE_SINCE_LAST_FUNDING_SPREAD_RETREAT,
|
|
13
16
|
} from '../constants/numericConstants';
|
|
14
17
|
import {
|
|
15
18
|
AMM,
|
|
@@ -19,7 +22,7 @@ import {
|
|
|
19
22
|
isVariant,
|
|
20
23
|
} from '../types';
|
|
21
24
|
import { assert } from '../assert/assert';
|
|
22
|
-
import { squareRootBN, standardizeBaseAssetAmount } from '..';
|
|
25
|
+
import { squareRootBN, clampBN, standardizeBaseAssetAmount } from '..';
|
|
23
26
|
|
|
24
27
|
import { OraclePriceData } from '../oracles/types';
|
|
25
28
|
import {
|
|
@@ -28,6 +31,8 @@ import {
|
|
|
28
31
|
calculateBudgetedPeg,
|
|
29
32
|
} from './repeg';
|
|
30
33
|
|
|
34
|
+
import { calculateLiveOracleStd } from './oracles';
|
|
35
|
+
|
|
31
36
|
export function calculatePegFromTargetPrice(
|
|
32
37
|
targetPrice: BN,
|
|
33
38
|
baseAssetReserve: BN,
|
|
@@ -63,7 +68,8 @@ export function calculateOptimalPegAndBudget(
|
|
|
63
68
|
const totalFeeLB = amm.totalExchangeFee.div(new BN(2));
|
|
64
69
|
const budget = BN.max(ZERO, amm.totalFeeMinusDistributions.sub(totalFeeLB));
|
|
65
70
|
if (budget.lt(prePegCost)) {
|
|
66
|
-
const
|
|
71
|
+
const halfMaxPriceSpread = new BN(amm.maxSpread)
|
|
72
|
+
.div(new BN(2))
|
|
67
73
|
.mul(targetPrice)
|
|
68
74
|
.div(BID_ASK_SPREAD_PRECISION);
|
|
69
75
|
|
|
@@ -72,8 +78,8 @@ export function calculateOptimalPegAndBudget(
|
|
|
72
78
|
let newBudget: BN;
|
|
73
79
|
const targetPriceGap = reservePriceBefore.sub(targetPrice);
|
|
74
80
|
|
|
75
|
-
if (targetPriceGap.abs().gt(
|
|
76
|
-
const markAdj = targetPriceGap.abs().sub(
|
|
81
|
+
if (targetPriceGap.abs().gt(halfMaxPriceSpread)) {
|
|
82
|
+
const markAdj = targetPriceGap.abs().sub(halfMaxPriceSpread);
|
|
77
83
|
|
|
78
84
|
if (targetPriceGap.lt(new BN(0))) {
|
|
79
85
|
newTargetPrice = reservePriceBefore.add(markAdj);
|
|
@@ -173,6 +179,8 @@ export function calculateUpdatedAMM(
|
|
|
173
179
|
|
|
174
180
|
newAmm.totalFeeMinusDistributions =
|
|
175
181
|
newAmm.totalFeeMinusDistributions.sub(prepegCost);
|
|
182
|
+
newAmm.netRevenueSinceLastFunding =
|
|
183
|
+
newAmm.netRevenueSinceLastFunding.sub(prepegCost);
|
|
176
184
|
|
|
177
185
|
return newAmm;
|
|
178
186
|
}
|
|
@@ -183,11 +191,15 @@ export function calculateUpdatedAMMSpreadReserves(
|
|
|
183
191
|
oraclePriceData: OraclePriceData
|
|
184
192
|
): { baseAssetReserve: BN; quoteAssetReserve: BN; sqrtK: BN; newPeg: BN } {
|
|
185
193
|
const newAmm = calculateUpdatedAMM(amm, oraclePriceData);
|
|
186
|
-
const
|
|
194
|
+
const [shortReserves, longReserves] = calculateSpreadReserves(
|
|
187
195
|
newAmm,
|
|
188
|
-
direction,
|
|
189
196
|
oraclePriceData
|
|
190
197
|
);
|
|
198
|
+
|
|
199
|
+
const dirReserves = isVariant(direction, 'long')
|
|
200
|
+
? longReserves
|
|
201
|
+
: shortReserves;
|
|
202
|
+
|
|
191
203
|
const result = {
|
|
192
204
|
baseAssetReserve: dirReserves.baseAssetReserve,
|
|
193
205
|
quoteAssetReserve: dirReserves.quoteAssetReserve,
|
|
@@ -210,14 +222,8 @@ export function calculateBidAskPrice(
|
|
|
210
222
|
newAmm = amm;
|
|
211
223
|
}
|
|
212
224
|
|
|
213
|
-
const askReserves = calculateSpreadReserves(
|
|
225
|
+
const [bidReserves, askReserves] = calculateSpreadReserves(
|
|
214
226
|
newAmm,
|
|
215
|
-
PositionDirection.LONG,
|
|
216
|
-
oraclePriceData
|
|
217
|
-
);
|
|
218
|
-
const bidReserves = calculateSpreadReserves(
|
|
219
|
-
newAmm,
|
|
220
|
-
PositionDirection.SHORT,
|
|
221
227
|
oraclePriceData
|
|
222
228
|
);
|
|
223
229
|
|
|
@@ -315,14 +321,14 @@ export function calculateMarketOpenBidAsk(
|
|
|
315
321
|
): [BN, BN] {
|
|
316
322
|
// open orders
|
|
317
323
|
let openAsks;
|
|
318
|
-
if (maxBaseAssetReserve
|
|
324
|
+
if (maxBaseAssetReserve.gt(baseAssetReserve)) {
|
|
319
325
|
openAsks = maxBaseAssetReserve.sub(baseAssetReserve).mul(new BN(-1));
|
|
320
326
|
} else {
|
|
321
327
|
openAsks = ZERO;
|
|
322
328
|
}
|
|
323
329
|
|
|
324
330
|
let openBids;
|
|
325
|
-
if (minBaseAssetReserve
|
|
331
|
+
if (minBaseAssetReserve.lt(baseAssetReserve)) {
|
|
326
332
|
openBids = baseAssetReserve.sub(minBaseAssetReserve);
|
|
327
333
|
} else {
|
|
328
334
|
openBids = ZERO;
|
|
@@ -331,12 +337,18 @@ export function calculateMarketOpenBidAsk(
|
|
|
331
337
|
}
|
|
332
338
|
|
|
333
339
|
export function calculateInventoryScale(
|
|
334
|
-
|
|
340
|
+
baseAssetAmountWithAmm: BN,
|
|
335
341
|
baseAssetReserve: BN,
|
|
336
342
|
minBaseAssetReserve: BN,
|
|
337
|
-
maxBaseAssetReserve: BN
|
|
343
|
+
maxBaseAssetReserve: BN,
|
|
344
|
+
directionalSpread: number,
|
|
345
|
+
maxSpread: number
|
|
338
346
|
): number {
|
|
339
|
-
|
|
347
|
+
if (baseAssetAmountWithAmm.eq(ZERO)) {
|
|
348
|
+
return 0;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
const defaultLargeBidAskFactor = BID_ASK_SPREAD_PRECISION.mul(new BN(10));
|
|
340
352
|
// inventory skew
|
|
341
353
|
const [openBids, openAsks] = calculateMarketOpenBidAsk(
|
|
342
354
|
baseAssetReserve,
|
|
@@ -348,13 +360,27 @@ export function calculateInventoryScale(
|
|
|
348
360
|
new BN(1),
|
|
349
361
|
BN.min(openBids.abs(), openAsks.abs())
|
|
350
362
|
);
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
363
|
+
|
|
364
|
+
const inventoryScaleMax =
|
|
365
|
+
BN.max(
|
|
366
|
+
defaultLargeBidAskFactor,
|
|
367
|
+
new BN(maxSpread / 2)
|
|
368
|
+
.mul(BID_ASK_SPREAD_PRECISION)
|
|
369
|
+
.div(new BN(Math.max(directionalSpread, 1)))
|
|
355
370
|
).toNumber() / BID_ASK_SPREAD_PRECISION.toNumber();
|
|
356
371
|
|
|
357
|
-
|
|
372
|
+
const inventoryScale =
|
|
373
|
+
baseAssetAmountWithAmm
|
|
374
|
+
.mul(BN.max(baseAssetAmountWithAmm.abs(), BASE_PRECISION))
|
|
375
|
+
.div(BASE_PRECISION)
|
|
376
|
+
.mul(defaultLargeBidAskFactor)
|
|
377
|
+
.div(minSideLiquidity)
|
|
378
|
+
.abs()
|
|
379
|
+
.toNumber() / BID_ASK_SPREAD_PRECISION.toNumber();
|
|
380
|
+
|
|
381
|
+
const inventorySpreadScale = Math.min(inventoryScaleMax, 1 + inventoryScale);
|
|
382
|
+
|
|
383
|
+
return inventorySpreadScale;
|
|
358
384
|
}
|
|
359
385
|
|
|
360
386
|
export function calculateEffectiveLeverage(
|
|
@@ -392,6 +418,48 @@ export function calculateMaxSpread(marginRatioInitial: number): number {
|
|
|
392
418
|
return maxTargetSpread;
|
|
393
419
|
}
|
|
394
420
|
|
|
421
|
+
export function calculateVolSpreadBN(
|
|
422
|
+
lastOracleConfPct: BN,
|
|
423
|
+
reservePrice: BN,
|
|
424
|
+
markStd: BN,
|
|
425
|
+
oracleStd: BN,
|
|
426
|
+
longIntensity: BN,
|
|
427
|
+
shortIntensity: BN,
|
|
428
|
+
volume24H: BN
|
|
429
|
+
): [BN, BN] {
|
|
430
|
+
const marketAvgStdPct = markStd
|
|
431
|
+
.add(oracleStd)
|
|
432
|
+
.mul(PERCENTAGE_PRECISION)
|
|
433
|
+
.div(reservePrice)
|
|
434
|
+
.div(new BN(2));
|
|
435
|
+
const volSpread = BN.max(lastOracleConfPct, marketAvgStdPct.div(new BN(2)));
|
|
436
|
+
|
|
437
|
+
const clampMin = PERCENTAGE_PRECISION.div(new BN(100));
|
|
438
|
+
const clampMax = PERCENTAGE_PRECISION.mul(new BN(16)).div(new BN(10));
|
|
439
|
+
|
|
440
|
+
const longVolSpreadFactor = clampBN(
|
|
441
|
+
longIntensity.mul(PERCENTAGE_PRECISION).div(BN.max(ONE, volume24H)),
|
|
442
|
+
clampMin,
|
|
443
|
+
clampMax
|
|
444
|
+
);
|
|
445
|
+
const shortVolSpreadFactor = clampBN(
|
|
446
|
+
shortIntensity.mul(PERCENTAGE_PRECISION).div(BN.max(ONE, volume24H)),
|
|
447
|
+
clampMin,
|
|
448
|
+
clampMax
|
|
449
|
+
);
|
|
450
|
+
|
|
451
|
+
const longVolSpread = BN.max(
|
|
452
|
+
lastOracleConfPct,
|
|
453
|
+
volSpread.mul(longVolSpreadFactor).div(PERCENTAGE_PRECISION)
|
|
454
|
+
);
|
|
455
|
+
const shortVolSpread = BN.max(
|
|
456
|
+
lastOracleConfPct,
|
|
457
|
+
volSpread.mul(shortVolSpreadFactor).div(PERCENTAGE_PRECISION)
|
|
458
|
+
);
|
|
459
|
+
|
|
460
|
+
return [longVolSpread, shortVolSpread];
|
|
461
|
+
}
|
|
462
|
+
|
|
395
463
|
export function calculateSpreadBN(
|
|
396
464
|
baseSpread: number,
|
|
397
465
|
lastOracleReservePriceSpreadPct: BN,
|
|
@@ -400,27 +468,43 @@ export function calculateSpreadBN(
|
|
|
400
468
|
quoteAssetReserve: BN,
|
|
401
469
|
terminalQuoteAssetReserve: BN,
|
|
402
470
|
pegMultiplier: BN,
|
|
403
|
-
|
|
471
|
+
baseAssetAmountWithAmm: BN,
|
|
404
472
|
reservePrice: BN,
|
|
405
473
|
totalFeeMinusDistributions: BN,
|
|
474
|
+
netRevenueSinceLastFunding: BN,
|
|
406
475
|
baseAssetReserve: BN,
|
|
407
476
|
minBaseAssetReserve: BN,
|
|
408
|
-
maxBaseAssetReserve: BN
|
|
477
|
+
maxBaseAssetReserve: BN,
|
|
478
|
+
markStd: BN,
|
|
479
|
+
oracleStd: BN,
|
|
480
|
+
longIntensity: BN,
|
|
481
|
+
shortIntensity: BN,
|
|
482
|
+
volume24H: BN
|
|
409
483
|
): [number, number] {
|
|
410
|
-
|
|
411
|
-
|
|
484
|
+
const [longVolSpread, shortVolSpread] = calculateVolSpreadBN(
|
|
485
|
+
lastOracleConfPct,
|
|
486
|
+
reservePrice,
|
|
487
|
+
markStd,
|
|
488
|
+
oracleStd,
|
|
489
|
+
longIntensity,
|
|
490
|
+
shortIntensity,
|
|
491
|
+
volume24H
|
|
492
|
+
);
|
|
412
493
|
|
|
413
|
-
|
|
494
|
+
let longSpread = Math.max(baseSpread / 2, longVolSpread.toNumber());
|
|
495
|
+
let shortSpread = Math.max(baseSpread / 2, shortVolSpread.toNumber());
|
|
496
|
+
|
|
497
|
+
if (lastOracleReservePriceSpreadPct.gt(ZERO)) {
|
|
414
498
|
shortSpread = Math.max(
|
|
415
499
|
shortSpread,
|
|
416
500
|
lastOracleReservePriceSpreadPct.abs().toNumber() +
|
|
417
|
-
|
|
501
|
+
shortVolSpread.toNumber()
|
|
418
502
|
);
|
|
419
503
|
} else if (lastOracleReservePriceSpreadPct.lt(ZERO)) {
|
|
420
504
|
longSpread = Math.max(
|
|
421
505
|
longSpread,
|
|
422
506
|
lastOracleReservePriceSpreadPct.abs().toNumber() +
|
|
423
|
-
|
|
507
|
+
longVolSpread.toNumber()
|
|
424
508
|
);
|
|
425
509
|
}
|
|
426
510
|
|
|
@@ -429,57 +513,75 @@ export function calculateSpreadBN(
|
|
|
429
513
|
lastOracleReservePriceSpreadPct.abs().toNumber()
|
|
430
514
|
);
|
|
431
515
|
|
|
432
|
-
const
|
|
433
|
-
|
|
434
|
-
const inventoryScale = calculateInventoryScale(
|
|
435
|
-
netBaseAssetAmount,
|
|
516
|
+
const inventorySpreadScale = calculateInventoryScale(
|
|
517
|
+
baseAssetAmountWithAmm,
|
|
436
518
|
baseAssetReserve,
|
|
437
519
|
minBaseAssetReserve,
|
|
438
|
-
maxBaseAssetReserve
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
MAX_BID_ASK_INVENTORY_SKEW_FACTOR,
|
|
442
|
-
1 + inventoryScale
|
|
520
|
+
maxBaseAssetReserve,
|
|
521
|
+
baseAssetAmountWithAmm.gt(ZERO) ? longSpread : shortSpread,
|
|
522
|
+
maxTargetSpread
|
|
443
523
|
);
|
|
444
524
|
|
|
445
|
-
if (
|
|
525
|
+
if (baseAssetAmountWithAmm.gt(ZERO)) {
|
|
446
526
|
longSpread *= inventorySpreadScale;
|
|
447
|
-
} else if (
|
|
527
|
+
} else if (baseAssetAmountWithAmm.lt(ZERO)) {
|
|
448
528
|
shortSpread *= inventorySpreadScale;
|
|
449
529
|
}
|
|
450
530
|
|
|
451
|
-
const
|
|
452
|
-
baseSpread,
|
|
453
|
-
quoteAssetReserve,
|
|
454
|
-
terminalQuoteAssetReserve,
|
|
455
|
-
pegMultiplier,
|
|
456
|
-
netBaseAssetAmount,
|
|
457
|
-
reservePrice,
|
|
458
|
-
totalFeeMinusDistributions
|
|
459
|
-
);
|
|
460
|
-
|
|
531
|
+
const MAX_SPREAD_SCALE = 10;
|
|
461
532
|
if (totalFeeMinusDistributions.gt(ZERO)) {
|
|
462
|
-
const
|
|
463
|
-
|
|
464
|
-
|
|
533
|
+
const effectiveLeverage = calculateEffectiveLeverage(
|
|
534
|
+
baseSpread,
|
|
535
|
+
quoteAssetReserve,
|
|
536
|
+
terminalQuoteAssetReserve,
|
|
537
|
+
pegMultiplier,
|
|
538
|
+
baseAssetAmountWithAmm,
|
|
539
|
+
reservePrice,
|
|
540
|
+
totalFeeMinusDistributions
|
|
465
541
|
);
|
|
466
|
-
|
|
542
|
+
const spreadScale = Math.min(MAX_SPREAD_SCALE, 1 + effectiveLeverage);
|
|
543
|
+
if (baseAssetAmountWithAmm.gt(ZERO)) {
|
|
467
544
|
longSpread *= spreadScale;
|
|
468
545
|
} else {
|
|
469
546
|
shortSpread *= spreadScale;
|
|
470
547
|
}
|
|
471
548
|
} else {
|
|
472
|
-
longSpread *=
|
|
473
|
-
shortSpread *=
|
|
549
|
+
longSpread *= MAX_SPREAD_SCALE;
|
|
550
|
+
shortSpread *= MAX_SPREAD_SCALE;
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
if (
|
|
554
|
+
netRevenueSinceLastFunding.lt(
|
|
555
|
+
DEFAULT_REVENUE_SINCE_LAST_FUNDING_SPREAD_RETREAT
|
|
556
|
+
)
|
|
557
|
+
) {
|
|
558
|
+
const retreatAmount = Math.min(
|
|
559
|
+
maxTargetSpread / 10,
|
|
560
|
+
Math.floor(
|
|
561
|
+
(baseSpread * netRevenueSinceLastFunding.abs().toNumber()) /
|
|
562
|
+
DEFAULT_REVENUE_SINCE_LAST_FUNDING_SPREAD_RETREAT.toNumber()
|
|
563
|
+
)
|
|
564
|
+
);
|
|
565
|
+
const halfRetreatAmount = Math.floor(retreatAmount / 2);
|
|
566
|
+
if (baseAssetAmountWithAmm.gt(ZERO)) {
|
|
567
|
+
longSpread += retreatAmount;
|
|
568
|
+
shortSpread += halfRetreatAmount;
|
|
569
|
+
} else if (baseAssetAmountWithAmm.lt(ZERO)) {
|
|
570
|
+
longSpread += halfRetreatAmount;
|
|
571
|
+
shortSpread += retreatAmount;
|
|
572
|
+
} else {
|
|
573
|
+
longSpread += halfRetreatAmount;
|
|
574
|
+
shortSpread += halfRetreatAmount;
|
|
575
|
+
}
|
|
474
576
|
}
|
|
475
577
|
|
|
476
578
|
const totalSpread = longSpread + shortSpread;
|
|
477
579
|
if (totalSpread > maxTargetSpread) {
|
|
478
580
|
if (longSpread > shortSpread) {
|
|
479
|
-
longSpread = Math.
|
|
581
|
+
longSpread = Math.ceil((longSpread * maxTargetSpread) / totalSpread);
|
|
480
582
|
shortSpread = maxTargetSpread - longSpread;
|
|
481
583
|
} else {
|
|
482
|
-
shortSpread = Math.
|
|
584
|
+
shortSpread = Math.ceil((shortSpread * maxTargetSpread) / totalSpread);
|
|
483
585
|
longSpread = maxTargetSpread - shortSpread;
|
|
484
586
|
}
|
|
485
587
|
}
|
|
@@ -489,11 +591,10 @@ export function calculateSpreadBN(
|
|
|
489
591
|
|
|
490
592
|
export function calculateSpread(
|
|
491
593
|
amm: AMM,
|
|
492
|
-
direction: PositionDirection,
|
|
493
594
|
oraclePriceData: OraclePriceData
|
|
494
|
-
): number {
|
|
595
|
+
): [number, number] {
|
|
495
596
|
if (amm.baseSpread == 0 || amm.curveUpdateIntensity == 0) {
|
|
496
|
-
return amm.baseSpread / 2;
|
|
597
|
+
return [amm.baseSpread / 2, amm.baseSpread / 2];
|
|
497
598
|
}
|
|
498
599
|
|
|
499
600
|
const reservePrice = calculatePrice(
|
|
@@ -504,7 +605,6 @@ export function calculateSpread(
|
|
|
504
605
|
|
|
505
606
|
const targetPrice = oraclePriceData?.price || reservePrice;
|
|
506
607
|
const confInterval = oraclePriceData.confidence || ZERO;
|
|
507
|
-
|
|
508
608
|
const targetMarkSpreadPct = reservePrice
|
|
509
609
|
.sub(targetPrice)
|
|
510
610
|
.mul(BID_ASK_SPREAD_PRECISION)
|
|
@@ -514,6 +614,9 @@ export function calculateSpread(
|
|
|
514
614
|
.mul(BID_ASK_SPREAD_PRECISION)
|
|
515
615
|
.div(reservePrice);
|
|
516
616
|
|
|
617
|
+
const now = new BN(new Date().getTime() / 1000); //todo
|
|
618
|
+
const liveOracleStd = calculateLiveOracleStd(amm, oraclePriceData, now);
|
|
619
|
+
|
|
517
620
|
const [longSpread, shortSpread] = calculateSpreadBN(
|
|
518
621
|
amm.baseSpread,
|
|
519
622
|
targetMarkSpreadPct,
|
|
@@ -525,55 +628,70 @@ export function calculateSpread(
|
|
|
525
628
|
amm.baseAssetAmountWithAmm,
|
|
526
629
|
reservePrice,
|
|
527
630
|
amm.totalFeeMinusDistributions,
|
|
631
|
+
amm.netRevenueSinceLastFunding,
|
|
528
632
|
amm.baseAssetReserve,
|
|
529
633
|
amm.minBaseAssetReserve,
|
|
530
|
-
amm.maxBaseAssetReserve
|
|
634
|
+
amm.maxBaseAssetReserve,
|
|
635
|
+
amm.markStd,
|
|
636
|
+
liveOracleStd,
|
|
637
|
+
amm.longIntensityVolume,
|
|
638
|
+
amm.shortIntensityVolume,
|
|
639
|
+
amm.volume24H
|
|
531
640
|
);
|
|
532
641
|
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
if (isVariant(direction, 'long')) {
|
|
536
|
-
spread = longSpread;
|
|
537
|
-
} else {
|
|
538
|
-
spread = shortSpread;
|
|
539
|
-
}
|
|
540
|
-
|
|
541
|
-
return spread;
|
|
642
|
+
return [longSpread, shortSpread];
|
|
542
643
|
}
|
|
543
644
|
|
|
544
645
|
export function calculateSpreadReserves(
|
|
545
646
|
amm: AMM,
|
|
546
|
-
direction: PositionDirection,
|
|
547
647
|
oraclePriceData: OraclePriceData
|
|
548
|
-
)
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
648
|
+
) {
|
|
649
|
+
function calculateSpreadReserve(
|
|
650
|
+
spread: number,
|
|
651
|
+
direction: PositionDirection,
|
|
652
|
+
amm: AMM
|
|
653
|
+
): {
|
|
654
|
+
baseAssetReserve;
|
|
655
|
+
quoteAssetReserve;
|
|
656
|
+
} {
|
|
657
|
+
if (spread === 0) {
|
|
658
|
+
return {
|
|
659
|
+
baseAssetReserve: amm.baseAssetReserve,
|
|
660
|
+
quoteAssetReserve: amm.quoteAssetReserve,
|
|
661
|
+
};
|
|
662
|
+
}
|
|
553
663
|
|
|
554
|
-
|
|
664
|
+
const quoteAssetReserveDelta = amm.quoteAssetReserve.div(
|
|
665
|
+
BID_ASK_SPREAD_PRECISION.div(new BN(spread / 2))
|
|
666
|
+
);
|
|
667
|
+
|
|
668
|
+
let quoteAssetReserve;
|
|
669
|
+
if (isVariant(direction, 'long')) {
|
|
670
|
+
quoteAssetReserve = amm.quoteAssetReserve.add(quoteAssetReserveDelta);
|
|
671
|
+
} else {
|
|
672
|
+
quoteAssetReserve = amm.quoteAssetReserve.sub(quoteAssetReserveDelta);
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
const baseAssetReserve = amm.sqrtK.mul(amm.sqrtK).div(quoteAssetReserve);
|
|
555
676
|
return {
|
|
556
|
-
baseAssetReserve
|
|
557
|
-
quoteAssetReserve
|
|
677
|
+
baseAssetReserve,
|
|
678
|
+
quoteAssetReserve,
|
|
558
679
|
};
|
|
559
680
|
}
|
|
560
681
|
|
|
561
|
-
const
|
|
562
|
-
|
|
682
|
+
const [longSpread, shortSpread] = calculateSpread(amm, oraclePriceData);
|
|
683
|
+
const askReserves = calculateSpreadReserve(
|
|
684
|
+
longSpread,
|
|
685
|
+
PositionDirection.LONG,
|
|
686
|
+
amm
|
|
687
|
+
);
|
|
688
|
+
const bidReserves = calculateSpreadReserve(
|
|
689
|
+
shortSpread,
|
|
690
|
+
PositionDirection.SHORT,
|
|
691
|
+
amm
|
|
563
692
|
);
|
|
564
693
|
|
|
565
|
-
|
|
566
|
-
if (isVariant(direction, 'long')) {
|
|
567
|
-
quoteAssetReserve = amm.quoteAssetReserve.add(quoteAsserReserveDelta);
|
|
568
|
-
} else {
|
|
569
|
-
quoteAssetReserve = amm.quoteAssetReserve.sub(quoteAsserReserveDelta);
|
|
570
|
-
}
|
|
571
|
-
|
|
572
|
-
const baseAssetReserve = amm.sqrtK.mul(amm.sqrtK).div(quoteAssetReserve);
|
|
573
|
-
return {
|
|
574
|
-
baseAssetReserve,
|
|
575
|
-
quoteAssetReserve,
|
|
576
|
-
};
|
|
694
|
+
return [bidReserves, askReserves];
|
|
577
695
|
}
|
|
578
696
|
|
|
579
697
|
/**
|
|
@@ -665,12 +783,14 @@ export function calculateMaxBaseAssetAmountToTrade(
|
|
|
665
783
|
.div(PEG_PRECISION);
|
|
666
784
|
|
|
667
785
|
const newBaseAssetReserve = squareRootBN(newBaseAssetReserveSquared);
|
|
668
|
-
|
|
669
|
-
const baseAssetReserveBefore = calculateSpreadReserves(
|
|
786
|
+
const [shortSpreadReserves, longSpreadReserves] = calculateSpreadReserves(
|
|
670
787
|
amm,
|
|
671
|
-
direction,
|
|
672
788
|
oraclePriceData
|
|
673
|
-
)
|
|
789
|
+
);
|
|
790
|
+
|
|
791
|
+
const baseAssetReserveBefore: BN = isVariant(direction, 'long')
|
|
792
|
+
? longSpreadReserves.baseAssetReserve
|
|
793
|
+
: shortSpreadReserves.baseAssetReserve;
|
|
674
794
|
|
|
675
795
|
if (newBaseAssetReserve.gt(baseAssetReserveBefore)) {
|
|
676
796
|
return [
|
package/src/math/oracles.ts
CHANGED
|
@@ -88,3 +88,55 @@ export function isOracleTooDivergent(
|
|
|
88
88
|
|
|
89
89
|
return tooDivergent;
|
|
90
90
|
}
|
|
91
|
+
|
|
92
|
+
export function calculateLiveOracleTwap(
|
|
93
|
+
amm: AMM,
|
|
94
|
+
oraclePriceData: OraclePriceData,
|
|
95
|
+
now: BN
|
|
96
|
+
): BN {
|
|
97
|
+
const sinceLastUpdate = now.sub(
|
|
98
|
+
amm.historicalOracleData.lastOraclePriceTwapTs
|
|
99
|
+
);
|
|
100
|
+
const sinceStart = BN.max(ZERO, amm.fundingPeriod.sub(sinceLastUpdate));
|
|
101
|
+
|
|
102
|
+
const clampRange = amm.historicalOracleData.lastOraclePriceTwap.div(
|
|
103
|
+
new BN(3)
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
const clampedOraclePrice = BN.min(
|
|
107
|
+
amm.historicalOracleData.lastOraclePriceTwap.add(clampRange),
|
|
108
|
+
BN.max(
|
|
109
|
+
oraclePriceData.price,
|
|
110
|
+
amm.historicalOracleData.lastOraclePriceTwap.sub(clampRange)
|
|
111
|
+
)
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
const newOracleTwap = amm.historicalOracleData.lastOraclePriceTwap
|
|
115
|
+
.mul(sinceStart)
|
|
116
|
+
.add(clampedOraclePrice)
|
|
117
|
+
.mul(sinceLastUpdate)
|
|
118
|
+
.div(sinceStart.add(sinceLastUpdate));
|
|
119
|
+
|
|
120
|
+
return newOracleTwap;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export function calculateLiveOracleStd(
|
|
124
|
+
amm: AMM,
|
|
125
|
+
oraclePriceData: OraclePriceData,
|
|
126
|
+
now: BN
|
|
127
|
+
): BN {
|
|
128
|
+
const sinceLastUpdate = now.sub(
|
|
129
|
+
amm.historicalOracleData.lastOraclePriceTwapTs
|
|
130
|
+
);
|
|
131
|
+
const sinceStart = BN.max(ZERO, amm.fundingPeriod.sub(sinceLastUpdate));
|
|
132
|
+
|
|
133
|
+
const liveOracleTwap = calculateLiveOracleTwap(amm, oraclePriceData, now);
|
|
134
|
+
|
|
135
|
+
const priceDeltaVsTwap = oraclePriceData.price.sub(liveOracleTwap).abs();
|
|
136
|
+
|
|
137
|
+
const oracleStd = priceDeltaVsTwap.add(
|
|
138
|
+
amm.oracleStd.mul(sinceStart).div(sinceStart.add(sinceLastUpdate))
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
return oracleStd;
|
|
142
|
+
}
|
package/src/math/utils.ts
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { BN } from '../';
|
|
2
2
|
|
|
3
|
-
export
|
|
3
|
+
export function clampBN(x: BN, min: BN, max: BN): BN {
|
|
4
|
+
return BN.max(min, BN.min(x, max));
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export const squareRootBN = (n, closeness = new BN(1)): BN => {
|
|
4
8
|
// Assuming the sqrt of n as n only
|
|
5
9
|
let x = n;
|
|
6
10
|
|
package/src/types.ts
CHANGED
|
@@ -519,6 +519,8 @@ export type StateAccount = {
|
|
|
519
519
|
perpFeeStructure: FeeStructure;
|
|
520
520
|
spotFeeStructure: FeeStructure;
|
|
521
521
|
lpCooldownTime: BN;
|
|
522
|
+
initialPctToLiquidate: number;
|
|
523
|
+
liquidationDuration: number;
|
|
522
524
|
};
|
|
523
525
|
|
|
524
526
|
export type PerpMarketAccount = {
|
|
@@ -745,8 +747,8 @@ export type PerpPosition = {
|
|
|
745
747
|
settledPnl: BN;
|
|
746
748
|
lpShares: BN;
|
|
747
749
|
remainderBaseAssetAmount: number;
|
|
748
|
-
|
|
749
|
-
|
|
750
|
+
lastBaseAssetAmountPerLp: BN;
|
|
751
|
+
lastQuoteAssetAmountPerLp: BN;
|
|
750
752
|
};
|
|
751
753
|
|
|
752
754
|
export type UserStatsAccount = {
|
|
@@ -790,6 +792,8 @@ export type UserAccount = {
|
|
|
790
792
|
totalWithdraws: BN;
|
|
791
793
|
totalSocialLoss: BN;
|
|
792
794
|
cumulativePerpFunding: BN;
|
|
795
|
+
liquidationMarginFreed: BN;
|
|
796
|
+
liquidationStartSlot: BN;
|
|
793
797
|
};
|
|
794
798
|
|
|
795
799
|
export type SpotPosition = {
|
package/src/user.ts
CHANGED
|
@@ -25,6 +25,7 @@ import {
|
|
|
25
25
|
SPOT_MARKET_WEIGHT_PRECISION,
|
|
26
26
|
QUOTE_SPOT_MARKET_INDEX,
|
|
27
27
|
TEN,
|
|
28
|
+
OPEN_ORDER_MARGIN_REQUIREMENT,
|
|
28
29
|
} from './constants/numericConstants';
|
|
29
30
|
import {
|
|
30
31
|
UserAccountSubscriber,
|
|
@@ -163,8 +164,8 @@ export class User {
|
|
|
163
164
|
openAsks: ZERO,
|
|
164
165
|
settledPnl: ZERO,
|
|
165
166
|
lpShares: ZERO,
|
|
166
|
-
|
|
167
|
-
|
|
167
|
+
lastBaseAssetAmountPerLp: ZERO,
|
|
168
|
+
lastQuoteAssetAmountPerLp: ZERO,
|
|
168
169
|
};
|
|
169
170
|
}
|
|
170
171
|
|
|
@@ -267,11 +268,11 @@ export class User {
|
|
|
267
268
|
const nShares = position.lpShares;
|
|
268
269
|
|
|
269
270
|
const deltaBaa = market.amm.baseAssetAmountPerLp
|
|
270
|
-
.sub(position.
|
|
271
|
+
.sub(position.lastBaseAssetAmountPerLp)
|
|
271
272
|
.mul(nShares)
|
|
272
273
|
.div(AMM_RESERVE_PRECISION);
|
|
273
274
|
const deltaQaa = market.amm.quoteAssetAmountPerLp
|
|
274
|
-
.sub(position.
|
|
275
|
+
.sub(position.lastQuoteAssetAmountPerLp)
|
|
275
276
|
.mul(nShares)
|
|
276
277
|
.div(AMM_RESERVE_PRECISION);
|
|
277
278
|
|
|
@@ -593,6 +594,10 @@ export class User {
|
|
|
593
594
|
newTotalLiabilityValue.add(weightedTokenValue);
|
|
594
595
|
}
|
|
595
596
|
|
|
597
|
+
newTotalLiabilityValue = newTotalLiabilityValue.add(
|
|
598
|
+
new BN(spotPosition.openOrders).mul(OPEN_ORDER_MARGIN_REQUIREMENT)
|
|
599
|
+
);
|
|
600
|
+
|
|
596
601
|
return newTotalLiabilityValue;
|
|
597
602
|
},
|
|
598
603
|
ZERO
|
|
@@ -893,6 +898,12 @@ export class User {
|
|
|
893
898
|
baseAssetValue = baseAssetValue
|
|
894
899
|
.mul(marginRatio)
|
|
895
900
|
.div(MARGIN_PRECISION);
|
|
901
|
+
|
|
902
|
+
if (includeOpenOrders) {
|
|
903
|
+
baseAssetValue = baseAssetValue.add(
|
|
904
|
+
new BN(perpPosition.openOrders).mul(OPEN_ORDER_MARGIN_REQUIREMENT)
|
|
905
|
+
);
|
|
906
|
+
}
|
|
896
907
|
}
|
|
897
908
|
|
|
898
909
|
return totalPerpValue.add(baseAssetValue);
|
|
@@ -1251,8 +1262,8 @@ export class User {
|
|
|
1251
1262
|
openAsks: new BN(0),
|
|
1252
1263
|
settledPnl: ZERO,
|
|
1253
1264
|
lpShares: ZERO,
|
|
1254
|
-
|
|
1255
|
-
|
|
1265
|
+
lastBaseAssetAmountPerLp: ZERO,
|
|
1266
|
+
lastQuoteAssetAmountPerLp: ZERO,
|
|
1256
1267
|
};
|
|
1257
1268
|
|
|
1258
1269
|
if (proposedBaseAssetAmount.eq(ZERO)) return new BN(-1);
|
package/tests/dlob/helpers.ts
CHANGED
|
@@ -37,8 +37,8 @@ export const mockPerpPosition: PerpPosition = {
|
|
|
37
37
|
settledPnl: new BN(0),
|
|
38
38
|
lpShares: new BN(0),
|
|
39
39
|
remainderBaseAssetAmount: 0,
|
|
40
|
-
|
|
41
|
-
|
|
40
|
+
lastBaseAssetAmountPerLp: new BN(0),
|
|
41
|
+
lastQuoteAssetAmountPerLp: new BN(0),
|
|
42
42
|
};
|
|
43
43
|
|
|
44
44
|
export const mockAMM: AMM = {
|
|
@@ -486,6 +486,8 @@ export const mockStateAccount: StateAccount = {
|
|
|
486
486
|
numberOfSpotMarkets: 0,
|
|
487
487
|
numberOfSubAccounts: new BN(0),
|
|
488
488
|
numberOfAuthorities: new BN(0),
|
|
489
|
+
initialPctToLiquidate: 0,
|
|
490
|
+
liquidationDuration: 0,
|
|
489
491
|
oracleGuardRails: {
|
|
490
492
|
priceDivergence: {
|
|
491
493
|
markOracleDivergenceNumerator: new BN(0),
|