@drift-labs/sdk 2.143.0-beta.7 → 2.143.0-beta.9
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/browser/adminClient.d.ts +2 -2
- package/lib/browser/adminClient.js +5 -5
- package/lib/browser/dlob/DLOB.d.ts +9 -3
- package/lib/browser/dlob/DLOB.js +10 -13
- package/lib/browser/idl/drift.json +18 -22
- package/lib/browser/math/auction.d.ts +3 -2
- package/lib/browser/math/auction.js +23 -4
- package/lib/browser/math/margin.d.ts +2 -1
- package/lib/browser/math/margin.js +31 -3
- package/lib/browser/math/market.js +38 -18
- package/lib/browser/math/oracles.d.ts +2 -1
- package/lib/browser/math/oracles.js +55 -1
- package/lib/browser/math/orders.d.ts +3 -2
- package/lib/browser/math/orders.js +13 -3
- package/lib/browser/types.d.ts +12 -1
- package/lib/browser/types.js +12 -1
- package/lib/node/adminClient.d.ts +2 -2
- package/lib/node/adminClient.d.ts.map +1 -1
- package/lib/node/adminClient.js +5 -5
- package/lib/node/dlob/DLOB.d.ts +9 -3
- package/lib/node/dlob/DLOB.d.ts.map +1 -1
- package/lib/node/dlob/DLOB.js +10 -13
- package/lib/node/idl/drift.json +18 -22
- package/lib/node/math/auction.d.ts +3 -2
- package/lib/node/math/auction.d.ts.map +1 -1
- package/lib/node/math/auction.js +23 -4
- package/lib/node/math/margin.d.ts +2 -1
- package/lib/node/math/margin.d.ts.map +1 -1
- package/lib/node/math/margin.js +31 -3
- package/lib/node/math/market.d.ts.map +1 -1
- package/lib/node/math/market.js +38 -18
- package/lib/node/math/oracles.d.ts +2 -1
- package/lib/node/math/oracles.d.ts.map +1 -1
- package/lib/node/math/oracles.js +55 -1
- package/lib/node/math/orders.d.ts +3 -2
- package/lib/node/math/orders.d.ts.map +1 -1
- package/lib/node/math/orders.js +13 -3
- package/lib/node/types.d.ts +12 -1
- package/lib/node/types.d.ts.map +1 -1
- package/lib/node/types.js +12 -1
- package/package.json +1 -1
- package/src/adminClient.ts +10 -10
- package/src/dlob/DLOB.ts +28 -15
- package/src/idl/drift.json +18 -22
- package/src/math/auction.ts +50 -7
- package/src/math/margin.ts +44 -5
- package/src/math/market.ts +61 -31
- package/src/math/oracles.ts +88 -0
- package/src/math/orders.ts +30 -2
- package/src/types.ts +13 -1
- package/tests/dlob/helpers.ts +4 -4
package/src/dlob/DLOB.ts
CHANGED
|
@@ -467,10 +467,6 @@ export class DLOB {
|
|
|
467
467
|
|
|
468
468
|
const isAmmPaused = ammPaused(stateAccount, marketAccount);
|
|
469
469
|
|
|
470
|
-
const minAuctionDuration = isVariant(marketType, 'perp')
|
|
471
|
-
? stateAccount.minPerpAuctionDuration
|
|
472
|
-
: 0;
|
|
473
|
-
|
|
474
470
|
const { makerRebateNumerator, makerRebateDenominator } =
|
|
475
471
|
this.getMakerRebate(marketType, stateAccount, marketAccount);
|
|
476
472
|
|
|
@@ -481,7 +477,8 @@ export class DLOB {
|
|
|
481
477
|
marketType,
|
|
482
478
|
oraclePriceData,
|
|
483
479
|
isAmmPaused,
|
|
484
|
-
|
|
480
|
+
stateAccount,
|
|
481
|
+
marketAccount,
|
|
485
482
|
fallbackAsk,
|
|
486
483
|
fallbackBid
|
|
487
484
|
);
|
|
@@ -493,7 +490,8 @@ export class DLOB {
|
|
|
493
490
|
marketType,
|
|
494
491
|
oraclePriceData,
|
|
495
492
|
isAmmPaused,
|
|
496
|
-
|
|
493
|
+
stateAccount,
|
|
494
|
+
marketAccount,
|
|
497
495
|
makerRebateNumerator,
|
|
498
496
|
makerRebateDenominator,
|
|
499
497
|
fallbackAsk,
|
|
@@ -597,7 +595,10 @@ export class DLOB {
|
|
|
597
595
|
? OraclePriceData
|
|
598
596
|
: MMOraclePriceData,
|
|
599
597
|
isAmmPaused: boolean,
|
|
600
|
-
|
|
598
|
+
stateAccount: StateAccount,
|
|
599
|
+
marketAccount: T extends { spot: unknown }
|
|
600
|
+
? SpotMarketAccount
|
|
601
|
+
: PerpMarketAccount,
|
|
601
602
|
makerRebateNumerator: number,
|
|
602
603
|
makerRebateDenominator: number,
|
|
603
604
|
fallbackAsk: BN | undefined,
|
|
@@ -636,7 +637,8 @@ export class DLOB {
|
|
|
636
637
|
(askPrice) => {
|
|
637
638
|
return askPrice.lte(fallbackBidWithBuffer);
|
|
638
639
|
},
|
|
639
|
-
|
|
640
|
+
stateAccount,
|
|
641
|
+
marketAccount
|
|
640
642
|
);
|
|
641
643
|
|
|
642
644
|
for (const askCrossingFallback of asksCrossingFallback) {
|
|
@@ -664,7 +666,8 @@ export class DLOB {
|
|
|
664
666
|
(bidPrice) => {
|
|
665
667
|
return bidPrice.gte(fallbackAskWithBuffer);
|
|
666
668
|
},
|
|
667
|
-
|
|
669
|
+
stateAccount,
|
|
670
|
+
marketAccount
|
|
668
671
|
);
|
|
669
672
|
|
|
670
673
|
for (const bidCrossingFallback of bidsCrossingFallback) {
|
|
@@ -683,7 +686,10 @@ export class DLOB {
|
|
|
683
686
|
? OraclePriceData
|
|
684
687
|
: MMOraclePriceData,
|
|
685
688
|
isAmmPaused: boolean,
|
|
686
|
-
|
|
689
|
+
state: StateAccount,
|
|
690
|
+
marketAccount: T extends { spot: unknown }
|
|
691
|
+
? SpotMarketAccount
|
|
692
|
+
: PerpMarketAccount,
|
|
687
693
|
fallbackAsk: BN | undefined,
|
|
688
694
|
fallbackBid?: BN | undefined
|
|
689
695
|
): NodeToFill[] {
|
|
@@ -736,7 +742,8 @@ export class DLOB {
|
|
|
736
742
|
(takerPrice) => {
|
|
737
743
|
return takerPrice === undefined || takerPrice.lte(fallbackBid);
|
|
738
744
|
},
|
|
739
|
-
|
|
745
|
+
state,
|
|
746
|
+
marketAccount
|
|
740
747
|
);
|
|
741
748
|
|
|
742
749
|
for (const takingAskCrossingFallback of takingAsksCrossingFallback) {
|
|
@@ -793,7 +800,8 @@ export class DLOB {
|
|
|
793
800
|
(takerPrice) => {
|
|
794
801
|
return takerPrice === undefined || takerPrice.gte(fallbackAsk);
|
|
795
802
|
},
|
|
796
|
-
|
|
803
|
+
state,
|
|
804
|
+
marketAccount
|
|
797
805
|
);
|
|
798
806
|
for (const marketBidCrossingFallback of takingBidsCrossingFallback) {
|
|
799
807
|
nodesToFill.push(marketBidCrossingFallback);
|
|
@@ -911,7 +919,10 @@ export class DLOB {
|
|
|
911
919
|
: MMOraclePriceData,
|
|
912
920
|
nodeGenerator: Generator<DLOBNode>,
|
|
913
921
|
doesCross: (nodePrice: BN | undefined) => boolean,
|
|
914
|
-
|
|
922
|
+
state: StateAccount,
|
|
923
|
+
marketAccount: T extends { spot: unknown }
|
|
924
|
+
? SpotMarketAccount
|
|
925
|
+
: PerpMarketAccount
|
|
915
926
|
): NodeToFill[] {
|
|
916
927
|
const nodesToFill = new Array<NodeToFill>();
|
|
917
928
|
|
|
@@ -934,8 +945,10 @@ export class DLOB {
|
|
|
934
945
|
isVariant(marketType, 'spot') ||
|
|
935
946
|
isFallbackAvailableLiquiditySource(
|
|
936
947
|
node.order,
|
|
937
|
-
|
|
938
|
-
slot
|
|
948
|
+
oraclePriceData as MMOraclePriceData,
|
|
949
|
+
slot,
|
|
950
|
+
state,
|
|
951
|
+
marketAccount as PerpMarketAccount
|
|
939
952
|
);
|
|
940
953
|
|
|
941
954
|
if (crosses && fallbackAvailable) {
|
package/src/idl/drift.json
CHANGED
|
@@ -6566,7 +6566,7 @@
|
|
|
6566
6566
|
]
|
|
6567
6567
|
},
|
|
6568
6568
|
{
|
|
6569
|
-
"name": "
|
|
6569
|
+
"name": "updatePerpMarketOracleLowRiskSlotDelayOverride",
|
|
6570
6570
|
"accounts": [
|
|
6571
6571
|
{
|
|
6572
6572
|
"name": "admin",
|
|
@@ -6586,7 +6586,7 @@
|
|
|
6586
6586
|
],
|
|
6587
6587
|
"args": [
|
|
6588
6588
|
{
|
|
6589
|
-
"name": "
|
|
6589
|
+
"name": "oracleLowRiskSlotDelayOverride",
|
|
6590
6590
|
"type": "i8"
|
|
6591
6591
|
}
|
|
6592
6592
|
]
|
|
@@ -11194,7 +11194,7 @@
|
|
|
11194
11194
|
"type": "i8"
|
|
11195
11195
|
},
|
|
11196
11196
|
{
|
|
11197
|
-
"name": "
|
|
11197
|
+
"name": "oracleLowRiskSlotDelayOverride",
|
|
11198
11198
|
"docs": [
|
|
11199
11199
|
"the override for the state.min_perp_auction_duration",
|
|
11200
11200
|
"0 is no override, -1 is disable speed bump, 1-100 is literal speed bump"
|
|
@@ -12278,7 +12278,17 @@
|
|
|
12278
12278
|
"name": "InsufficientDataPoints"
|
|
12279
12279
|
},
|
|
12280
12280
|
{
|
|
12281
|
-
"name": "StaleForAMM"
|
|
12281
|
+
"name": "StaleForAMM",
|
|
12282
|
+
"fields": [
|
|
12283
|
+
{
|
|
12284
|
+
"name": "immediate",
|
|
12285
|
+
"type": "bool"
|
|
12286
|
+
},
|
|
12287
|
+
{
|
|
12288
|
+
"name": "lowRisk",
|
|
12289
|
+
"type": "bool"
|
|
12290
|
+
}
|
|
12291
|
+
]
|
|
12282
12292
|
},
|
|
12283
12293
|
{
|
|
12284
12294
|
"name": "Valid"
|
|
@@ -12304,7 +12314,10 @@
|
|
|
12304
12314
|
"name": "FillOrderMatch"
|
|
12305
12315
|
},
|
|
12306
12316
|
{
|
|
12307
|
-
"name": "
|
|
12317
|
+
"name": "FillOrderAmmLowRisk"
|
|
12318
|
+
},
|
|
12319
|
+
{
|
|
12320
|
+
"name": "FillOrderAmmImmediate"
|
|
12308
12321
|
},
|
|
12309
12322
|
{
|
|
12310
12323
|
"name": "Liquidate"
|
|
@@ -12954,23 +12967,6 @@
|
|
|
12954
12967
|
]
|
|
12955
12968
|
}
|
|
12956
12969
|
},
|
|
12957
|
-
{
|
|
12958
|
-
"name": "AMMAvailability",
|
|
12959
|
-
"type": {
|
|
12960
|
-
"kind": "enum",
|
|
12961
|
-
"variants": [
|
|
12962
|
-
{
|
|
12963
|
-
"name": "Immediate"
|
|
12964
|
-
},
|
|
12965
|
-
{
|
|
12966
|
-
"name": "AfterMinDuration"
|
|
12967
|
-
},
|
|
12968
|
-
{
|
|
12969
|
-
"name": "Unavailable"
|
|
12970
|
-
}
|
|
12971
|
-
]
|
|
12972
|
-
}
|
|
12973
|
-
},
|
|
12974
12970
|
{
|
|
12975
12971
|
"name": "RevenueShareOrderBitFlag",
|
|
12976
12972
|
"type": {
|
package/src/math/auction.ts
CHANGED
|
@@ -1,4 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
isOneOfVariant,
|
|
3
|
+
isVariant,
|
|
4
|
+
OracleValidity,
|
|
5
|
+
Order,
|
|
6
|
+
PerpOperation,
|
|
7
|
+
PositionDirection,
|
|
8
|
+
StateAccount,
|
|
9
|
+
} from '../types';
|
|
2
10
|
import { BN } from '@coral-xyz/anchor';
|
|
3
11
|
import {
|
|
4
12
|
ONE,
|
|
@@ -8,6 +16,10 @@ import {
|
|
|
8
16
|
} from '../constants/numericConstants';
|
|
9
17
|
import { getVariant, OrderBitFlag, PerpMarketAccount } from '../types';
|
|
10
18
|
import { getPerpMarketTierNumber } from './tiers';
|
|
19
|
+
import { MMOraclePriceData } from '../oracles/types';
|
|
20
|
+
import { isLowRiskForAmm } from './orders';
|
|
21
|
+
import { getOracleValidity } from './oracles';
|
|
22
|
+
import { isOperationPaused } from './exchangeStatus';
|
|
11
23
|
|
|
12
24
|
export function isAuctionComplete(order: Order, slot: number): boolean {
|
|
13
25
|
if (order.auctionDuration === 0) {
|
|
@@ -19,18 +31,49 @@ export function isAuctionComplete(order: Order, slot: number): boolean {
|
|
|
19
31
|
|
|
20
32
|
export function isFallbackAvailableLiquiditySource(
|
|
21
33
|
order: Order,
|
|
22
|
-
|
|
23
|
-
slot: number
|
|
34
|
+
mmOraclePriceData: MMOraclePriceData,
|
|
35
|
+
slot: number,
|
|
36
|
+
state: StateAccount,
|
|
37
|
+
market: PerpMarketAccount,
|
|
38
|
+
isLiquidation?: boolean
|
|
24
39
|
): boolean {
|
|
25
|
-
if (
|
|
26
|
-
return
|
|
40
|
+
if (isOperationPaused(market.pausedOperations, PerpOperation.AMM_FILL)) {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// TODO: include too much drawdown check & mm oracle volatility
|
|
45
|
+
|
|
46
|
+
const oracleValidity = getOracleValidity(
|
|
47
|
+
market!,
|
|
48
|
+
{
|
|
49
|
+
price: mmOraclePriceData.price,
|
|
50
|
+
slot: mmOraclePriceData.slot,
|
|
51
|
+
confidence: mmOraclePriceData.confidence,
|
|
52
|
+
hasSufficientNumberOfDataPoints:
|
|
53
|
+
mmOraclePriceData.hasSufficientNumberOfDataPoints,
|
|
54
|
+
},
|
|
55
|
+
state.oracleGuardRails,
|
|
56
|
+
new BN(slot)
|
|
57
|
+
);
|
|
58
|
+
if (oracleValidity <= OracleValidity.StaleForAMMLowRisk) {
|
|
59
|
+
return false;
|
|
27
60
|
}
|
|
28
61
|
|
|
29
|
-
if (
|
|
62
|
+
if (oracleValidity == OracleValidity.Valid) {
|
|
30
63
|
return true;
|
|
31
64
|
}
|
|
32
65
|
|
|
33
|
-
|
|
66
|
+
const isOrderLowRiskForAmm = isLowRiskForAmm(
|
|
67
|
+
order,
|
|
68
|
+
mmOraclePriceData,
|
|
69
|
+
isLiquidation
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
if (!isOrderLowRiskForAmm) {
|
|
73
|
+
return false;
|
|
74
|
+
} else {
|
|
75
|
+
return true;
|
|
76
|
+
}
|
|
34
77
|
}
|
|
35
78
|
|
|
36
79
|
/**
|
package/src/math/margin.ts
CHANGED
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
MARGIN_PRECISION,
|
|
11
11
|
PRICE_PRECISION,
|
|
12
12
|
QUOTE_PRECISION,
|
|
13
|
+
PERCENTAGE_PRECISION,
|
|
13
14
|
} from '../constants/numericConstants';
|
|
14
15
|
import { BN } from '@coral-xyz/anchor';
|
|
15
16
|
import { OraclePriceData } from '../oracles/types';
|
|
@@ -32,7 +33,8 @@ export function calculateSizePremiumLiabilityWeight(
|
|
|
32
33
|
size: BN, // AMM_RESERVE_PRECISION
|
|
33
34
|
imfFactor: BN,
|
|
34
35
|
liabilityWeight: BN,
|
|
35
|
-
precision: BN
|
|
36
|
+
precision: BN,
|
|
37
|
+
isBounded = true
|
|
36
38
|
): BN {
|
|
37
39
|
if (imfFactor.eq(ZERO)) {
|
|
38
40
|
return liabilityWeight;
|
|
@@ -53,10 +55,13 @@ export function calculateSizePremiumLiabilityWeight(
|
|
|
53
55
|
.div(denom) // 1e5
|
|
54
56
|
);
|
|
55
57
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
sizePremiumLiabilityWeight
|
|
59
|
-
|
|
58
|
+
let maxLiabilityWeight;
|
|
59
|
+
if (isBounded) {
|
|
60
|
+
maxLiabilityWeight = BN.max(liabilityWeight, sizePremiumLiabilityWeight);
|
|
61
|
+
} else {
|
|
62
|
+
maxLiabilityWeight = sizePremiumLiabilityWeight;
|
|
63
|
+
}
|
|
64
|
+
|
|
60
65
|
return maxLiabilityWeight;
|
|
61
66
|
}
|
|
62
67
|
|
|
@@ -370,3 +375,37 @@ export function calculateUserMaxPerpOrderSize(
|
|
|
370
375
|
|
|
371
376
|
return user.getMaxTradeSizeUSDCForPerp(targetMarketIndex, tradeSide);
|
|
372
377
|
}
|
|
378
|
+
|
|
379
|
+
export function calcHighLeverageModeInitialMarginRatioFromSize(
|
|
380
|
+
preSizeAdjMarginRatio: BN,
|
|
381
|
+
sizeAdjMarginRatio: BN,
|
|
382
|
+
defaultMarginRatio: BN
|
|
383
|
+
): BN {
|
|
384
|
+
let result: BN;
|
|
385
|
+
|
|
386
|
+
if (sizeAdjMarginRatio.lt(preSizeAdjMarginRatio)) {
|
|
387
|
+
const sizePctDiscountFactor = PERCENTAGE_PRECISION.sub(
|
|
388
|
+
preSizeAdjMarginRatio
|
|
389
|
+
.sub(sizeAdjMarginRatio)
|
|
390
|
+
.mul(PERCENTAGE_PRECISION)
|
|
391
|
+
.div(preSizeAdjMarginRatio.div(new BN(5)))
|
|
392
|
+
);
|
|
393
|
+
|
|
394
|
+
const hlmMarginDelta = BN.max(
|
|
395
|
+
preSizeAdjMarginRatio.sub(defaultMarginRatio),
|
|
396
|
+
new BN(1)
|
|
397
|
+
);
|
|
398
|
+
|
|
399
|
+
const hlmMarginDeltaProportion = hlmMarginDelta
|
|
400
|
+
.mul(sizePctDiscountFactor)
|
|
401
|
+
.div(PERCENTAGE_PRECISION);
|
|
402
|
+
|
|
403
|
+
result = hlmMarginDeltaProportion.add(defaultMarginRatio);
|
|
404
|
+
} else if (sizeAdjMarginRatio.eq(preSizeAdjMarginRatio)) {
|
|
405
|
+
result = defaultMarginRatio;
|
|
406
|
+
} else {
|
|
407
|
+
result = sizeAdjMarginRatio;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
return result;
|
|
411
|
+
}
|
package/src/math/market.ts
CHANGED
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
import {
|
|
20
20
|
calculateSizeDiscountAssetWeight,
|
|
21
21
|
calculateSizePremiumLiabilityWeight,
|
|
22
|
+
calcHighLeverageModeInitialMarginRatioFromSize,
|
|
22
23
|
} from './margin';
|
|
23
24
|
import { MMOraclePriceData, OraclePriceData } from '../oracles/types';
|
|
24
25
|
import {
|
|
@@ -143,45 +144,74 @@ export function calculateMarketMarginRatio(
|
|
|
143
144
|
customMarginRatio = 0,
|
|
144
145
|
userHighLeverageMode = false
|
|
145
146
|
): number {
|
|
146
|
-
|
|
147
|
-
let marginRatioMaintenance;
|
|
147
|
+
if (market.status === 'Settlement') return 0;
|
|
148
148
|
|
|
149
|
-
|
|
149
|
+
const isHighLeverageUser =
|
|
150
150
|
userHighLeverageMode &&
|
|
151
151
|
market.highLeverageMarginRatioInitial > 0 &&
|
|
152
|
-
market.highLeverageMarginRatioMaintenance
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
152
|
+
market.highLeverageMarginRatioMaintenance > 0;
|
|
153
|
+
|
|
154
|
+
const marginRatioInitial = isHighLeverageUser
|
|
155
|
+
? market.highLeverageMarginRatioInitial
|
|
156
|
+
: market.marginRatioInitial;
|
|
157
|
+
|
|
158
|
+
const marginRatioMaintenance = isHighLeverageUser
|
|
159
|
+
? market.highLeverageMarginRatioMaintenance
|
|
160
|
+
: market.marginRatioMaintenance;
|
|
160
161
|
|
|
161
|
-
let
|
|
162
|
+
let defaultMarginRatio: number;
|
|
162
163
|
switch (marginCategory) {
|
|
163
|
-
case 'Initial':
|
|
164
|
-
|
|
165
|
-
marginRatio = Math.max(
|
|
166
|
-
calculateSizePremiumLiabilityWeight(
|
|
167
|
-
size,
|
|
168
|
-
new BN(market.imfFactor),
|
|
169
|
-
new BN(marginRationInitial),
|
|
170
|
-
MARGIN_PRECISION
|
|
171
|
-
).toNumber(),
|
|
172
|
-
customMarginRatio
|
|
173
|
-
);
|
|
164
|
+
case 'Initial':
|
|
165
|
+
defaultMarginRatio = marginRatioInitial;
|
|
174
166
|
break;
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
marginRatio = calculateSizePremiumLiabilityWeight(
|
|
178
|
-
size,
|
|
179
|
-
new BN(market.imfFactor),
|
|
180
|
-
new BN(marginRatioMaintenance),
|
|
181
|
-
MARGIN_PRECISION
|
|
182
|
-
).toNumber();
|
|
167
|
+
case 'Maintenance':
|
|
168
|
+
defaultMarginRatio = marginRatioMaintenance;
|
|
183
169
|
break;
|
|
170
|
+
default:
|
|
171
|
+
throw new Error('Invalid margin category');
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
let marginRatio: number;
|
|
175
|
+
|
|
176
|
+
if (isHighLeverageUser && marginCategory !== 'Maintenance') {
|
|
177
|
+
// Use ordinary-mode initial/fill ratios for size-adjusted calc
|
|
178
|
+
let preSizeAdjMarginRatio: number;
|
|
179
|
+
switch (marginCategory) {
|
|
180
|
+
case 'Initial':
|
|
181
|
+
preSizeAdjMarginRatio = market.marginRatioInitial;
|
|
182
|
+
break;
|
|
183
|
+
default:
|
|
184
|
+
preSizeAdjMarginRatio = marginRatioMaintenance;
|
|
185
|
+
break;
|
|
184
186
|
}
|
|
187
|
+
|
|
188
|
+
const sizeAdjMarginRatio = calculateSizePremiumLiabilityWeight(
|
|
189
|
+
size,
|
|
190
|
+
new BN(market.imfFactor),
|
|
191
|
+
new BN(preSizeAdjMarginRatio),
|
|
192
|
+
MARGIN_PRECISION,
|
|
193
|
+
false
|
|
194
|
+
).toNumber();
|
|
195
|
+
|
|
196
|
+
marginRatio = calcHighLeverageModeInitialMarginRatioFromSize(
|
|
197
|
+
new BN(preSizeAdjMarginRatio),
|
|
198
|
+
new BN(sizeAdjMarginRatio),
|
|
199
|
+
new BN(defaultMarginRatio)
|
|
200
|
+
).toNumber();
|
|
201
|
+
} else {
|
|
202
|
+
const sizeAdjMarginRatio = calculateSizePremiumLiabilityWeight(
|
|
203
|
+
size,
|
|
204
|
+
new BN(market.imfFactor),
|
|
205
|
+
new BN(defaultMarginRatio),
|
|
206
|
+
MARGIN_PRECISION,
|
|
207
|
+
true
|
|
208
|
+
).toNumber();
|
|
209
|
+
|
|
210
|
+
marginRatio = Math.max(defaultMarginRatio, sizeAdjMarginRatio);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
if (marginCategory === 'Initial') {
|
|
214
|
+
marginRatio = Math.max(marginRatio, customMarginRatio);
|
|
185
215
|
}
|
|
186
216
|
|
|
187
217
|
return marginRatio;
|
package/src/math/oracles.ts
CHANGED
|
@@ -3,7 +3,9 @@ import {
|
|
|
3
3
|
HistoricalOracleData,
|
|
4
4
|
OracleGuardRails,
|
|
5
5
|
OracleSource,
|
|
6
|
+
OracleValidity,
|
|
6
7
|
PerpMarketAccount,
|
|
8
|
+
isOneOfVariant,
|
|
7
9
|
isVariant,
|
|
8
10
|
} from '../types';
|
|
9
11
|
import { OraclePriceData } from '../oracles/types';
|
|
@@ -14,6 +16,7 @@ import {
|
|
|
14
16
|
ZERO,
|
|
15
17
|
FIVE_MINUTE,
|
|
16
18
|
PERCENTAGE_PRECISION,
|
|
19
|
+
FIVE,
|
|
17
20
|
} from '../constants/numericConstants';
|
|
18
21
|
import { assert } from '../assert/assert';
|
|
19
22
|
import { BN } from '@coral-xyz/anchor';
|
|
@@ -51,6 +54,91 @@ export function getMaxConfidenceIntervalMultiplier(
|
|
|
51
54
|
return maxConfidenceIntervalMultiplier;
|
|
52
55
|
}
|
|
53
56
|
|
|
57
|
+
export function getOracleValidity(
|
|
58
|
+
market: PerpMarketAccount,
|
|
59
|
+
oraclePriceData: OraclePriceData,
|
|
60
|
+
oracleGuardRails: OracleGuardRails,
|
|
61
|
+
slot: BN,
|
|
62
|
+
oracleStalenessBuffer = FIVE
|
|
63
|
+
): OracleValidity {
|
|
64
|
+
const isNonPositive = oraclePriceData.price.lte(ZERO);
|
|
65
|
+
const isTooVolatile = BN.max(
|
|
66
|
+
oraclePriceData.price,
|
|
67
|
+
market.amm.historicalOracleData.lastOraclePriceTwap
|
|
68
|
+
)
|
|
69
|
+
.div(
|
|
70
|
+
BN.max(
|
|
71
|
+
ONE,
|
|
72
|
+
BN.min(
|
|
73
|
+
oraclePriceData.price,
|
|
74
|
+
market.amm.historicalOracleData.lastOraclePriceTwap
|
|
75
|
+
)
|
|
76
|
+
)
|
|
77
|
+
)
|
|
78
|
+
.gt(oracleGuardRails.validity.tooVolatileRatio);
|
|
79
|
+
|
|
80
|
+
const confPctOfPrice = oraclePriceData.confidence
|
|
81
|
+
.mul(BID_ASK_SPREAD_PRECISION)
|
|
82
|
+
.div(oraclePriceData.price);
|
|
83
|
+
const isConfTooLarge = confPctOfPrice.gt(
|
|
84
|
+
oracleGuardRails.validity.confidenceIntervalMaxSize.mul(
|
|
85
|
+
getMaxConfidenceIntervalMultiplier(market)
|
|
86
|
+
)
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
const oracleDelay = slot.sub(oraclePriceData.slot).sub(oracleStalenessBuffer);
|
|
90
|
+
|
|
91
|
+
let isStaleForAmmImmediate = true;
|
|
92
|
+
if (market.amm.oracleSlotDelayOverride != 0) {
|
|
93
|
+
isStaleForAmmImmediate = oracleDelay.gt(
|
|
94
|
+
BN.max(new BN(market.amm.oracleSlotDelayOverride), ZERO)
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
let isStaleForAmmLowRisk = false;
|
|
99
|
+
if (market.amm.oracleLowRiskSlotDelayOverride != 0) {
|
|
100
|
+
isStaleForAmmLowRisk = oracleDelay.gt(
|
|
101
|
+
BN.max(new BN(market.amm.oracleLowRiskSlotDelayOverride), ZERO)
|
|
102
|
+
);
|
|
103
|
+
} else {
|
|
104
|
+
isStaleForAmmLowRisk = oracleDelay.gt(
|
|
105
|
+
oracleGuardRails.validity.slotsBeforeStaleForAmm
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
let isStaleForMargin = oracleDelay.gt(
|
|
110
|
+
new BN(oracleGuardRails.validity.slotsBeforeStaleForMargin)
|
|
111
|
+
);
|
|
112
|
+
if (
|
|
113
|
+
isOneOfVariant(market.amm.oracleSource, [
|
|
114
|
+
'pythStableCoinPull',
|
|
115
|
+
'pythLazerStableCoin',
|
|
116
|
+
])
|
|
117
|
+
) {
|
|
118
|
+
isStaleForMargin = oracleDelay.gt(
|
|
119
|
+
new BN(oracleGuardRails.validity.slotsBeforeStaleForMargin).muln(3)
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (isNonPositive) {
|
|
124
|
+
return OracleValidity.NonPositive;
|
|
125
|
+
} else if (isTooVolatile) {
|
|
126
|
+
return OracleValidity.TooVolatile;
|
|
127
|
+
} else if (isConfTooLarge) {
|
|
128
|
+
return OracleValidity.TooUncertain;
|
|
129
|
+
} else if (isStaleForMargin) {
|
|
130
|
+
return OracleValidity.StaleForMargin;
|
|
131
|
+
} else if (!oraclePriceData.hasSufficientNumberOfDataPoints) {
|
|
132
|
+
return OracleValidity.InsufficientDataPoints;
|
|
133
|
+
} else if (isStaleForAmmLowRisk) {
|
|
134
|
+
return OracleValidity.StaleForAMMLowRisk;
|
|
135
|
+
} else if (isStaleForAmmImmediate) {
|
|
136
|
+
return OracleValidity.isStaleForAmmImmediate;
|
|
137
|
+
} else {
|
|
138
|
+
return OracleValidity.Valid;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
54
142
|
export function isOracleValid(
|
|
55
143
|
market: PerpMarketAccount,
|
|
56
144
|
oraclePriceData: OraclePriceData,
|
package/src/math/orders.ts
CHANGED
|
@@ -8,6 +8,8 @@ import {
|
|
|
8
8
|
PositionDirection,
|
|
9
9
|
ProtectedMakerParams,
|
|
10
10
|
MarketTypeStr,
|
|
11
|
+
OrderBitFlag,
|
|
12
|
+
StateAccount,
|
|
11
13
|
} from '../types';
|
|
12
14
|
import {
|
|
13
15
|
ZERO,
|
|
@@ -243,10 +245,16 @@ export function isFillableByVAMM(
|
|
|
243
245
|
mmOraclePriceData: MMOraclePriceData,
|
|
244
246
|
slot: number,
|
|
245
247
|
ts: number,
|
|
246
|
-
|
|
248
|
+
state: StateAccount
|
|
247
249
|
): boolean {
|
|
248
250
|
return (
|
|
249
|
-
(isFallbackAvailableLiquiditySource(
|
|
251
|
+
(isFallbackAvailableLiquiditySource(
|
|
252
|
+
order,
|
|
253
|
+
mmOraclePriceData,
|
|
254
|
+
slot,
|
|
255
|
+
state,
|
|
256
|
+
market
|
|
257
|
+
) &&
|
|
250
258
|
calculateBaseAssetAmountForAmmToFulfill(
|
|
251
259
|
order,
|
|
252
260
|
market,
|
|
@@ -257,6 +265,26 @@ export function isFillableByVAMM(
|
|
|
257
265
|
);
|
|
258
266
|
}
|
|
259
267
|
|
|
268
|
+
export function isLowRiskForAmm(
|
|
269
|
+
order: Order,
|
|
270
|
+
mmOraclePriceData: MMOraclePriceData,
|
|
271
|
+
isLiquidation?: boolean
|
|
272
|
+
): boolean {
|
|
273
|
+
if (isVariant(order.marketType, 'spot')) {
|
|
274
|
+
return false;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
const orderOlderThanOracleDelay = new BN(order.slot).lte(
|
|
278
|
+
mmOraclePriceData.slot
|
|
279
|
+
);
|
|
280
|
+
|
|
281
|
+
return (
|
|
282
|
+
orderOlderThanOracleDelay ||
|
|
283
|
+
isLiquidation ||
|
|
284
|
+
(order.bitFlags & OrderBitFlag.SafeTriggerOrder) !== 0
|
|
285
|
+
);
|
|
286
|
+
}
|
|
287
|
+
|
|
260
288
|
export function calculateBaseAssetAmountForAmmToFulfill(
|
|
261
289
|
order: Order,
|
|
262
290
|
market: PerpMarketAccount,
|
package/src/types.ts
CHANGED
|
@@ -1107,7 +1107,8 @@ export type AMM = {
|
|
|
1107
1107
|
quoteAssetAmountWithUnsettledLp: BN;
|
|
1108
1108
|
referencePriceOffset: number;
|
|
1109
1109
|
|
|
1110
|
-
|
|
1110
|
+
oracleLowRiskSlotDelayOverride: number;
|
|
1111
|
+
oracleSlotDelayOverride: number;
|
|
1111
1112
|
ammSpreadAdjustment: number;
|
|
1112
1113
|
ammInventorySpreadAdjustment: number;
|
|
1113
1114
|
|
|
@@ -1473,6 +1474,17 @@ export type OracleGuardRails = {
|
|
|
1473
1474
|
};
|
|
1474
1475
|
};
|
|
1475
1476
|
|
|
1477
|
+
export enum OracleValidity {
|
|
1478
|
+
NonPositive = 0,
|
|
1479
|
+
TooVolatile = 1,
|
|
1480
|
+
TooUncertain = 2,
|
|
1481
|
+
StaleForMargin = 3,
|
|
1482
|
+
InsufficientDataPoints = 4,
|
|
1483
|
+
StaleForAMMLowRisk = 5,
|
|
1484
|
+
isStaleForAmmImmediate = 6,
|
|
1485
|
+
Valid = 7,
|
|
1486
|
+
}
|
|
1487
|
+
|
|
1476
1488
|
export type PrelaunchOracle = {
|
|
1477
1489
|
price: BN;
|
|
1478
1490
|
maxPrice: BN;
|
package/tests/dlob/helpers.ts
CHANGED
|
@@ -145,7 +145,7 @@ export const mockAMM: AMM = {
|
|
|
145
145
|
quoteAssetAmountWithUnsettledLp: new BN(0),
|
|
146
146
|
referencePriceOffset: 0,
|
|
147
147
|
|
|
148
|
-
|
|
148
|
+
oracleLowRiskSlotDelayOverride: 0,
|
|
149
149
|
ammSpreadAdjustment: 0,
|
|
150
150
|
ammInventorySpreadAdjustment: 0,
|
|
151
151
|
mmOracleSequenceId: new BN(0),
|
|
@@ -672,9 +672,9 @@ export class MockUserMap implements UserMapInterface {
|
|
|
672
672
|
});
|
|
673
673
|
}
|
|
674
674
|
|
|
675
|
-
public async subscribe(): Promise<void> {}
|
|
675
|
+
public async subscribe(): Promise<void> { }
|
|
676
676
|
|
|
677
|
-
public async unsubscribe(): Promise<void> {}
|
|
677
|
+
public async unsubscribe(): Promise<void> { }
|
|
678
678
|
|
|
679
679
|
public async addPubkey(userAccountPublicKey: PublicKey): Promise<void> {
|
|
680
680
|
const user = new User({
|
|
@@ -733,7 +733,7 @@ export class MockUserMap implements UserMapInterface {
|
|
|
733
733
|
);
|
|
734
734
|
}
|
|
735
735
|
|
|
736
|
-
public async updateWithOrderRecord(_record: OrderRecord): Promise<void> {}
|
|
736
|
+
public async updateWithOrderRecord(_record: OrderRecord): Promise<void> { }
|
|
737
737
|
|
|
738
738
|
public values(): IterableIterator<User> {
|
|
739
739
|
return this.userMap.values();
|