@drift-labs/sdk 2.42.0-beta.6 → 2.42.0-beta.7
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/math/market.d.ts +1 -1
- package/lib/math/market.js +3 -2
- package/lib/math/spotMarket.d.ts +1 -1
- package/lib/math/spotMarket.js +9 -3
- package/lib/math/spotPosition.d.ts +3 -3
- package/lib/math/spotPosition.js +18 -7
- package/lib/user.d.ts +1 -1
- package/lib/user.js +29 -26
- package/package.json +1 -1
- package/src/math/market.ts +12 -7
- package/src/math/spotMarket.ts +13 -3
- package/src/math/spotPosition.ts +29 -7
- package/src/user.ts +64 -31
- package/tests/amm/test.ts +7 -5
- package/tests/auctions/test.ts +22 -11
- package/tests/dlob/helpers.ts +15 -13
- package/tests/tx/priorityFeeCalculator.ts +1 -1
- package/tests/user/test.ts +171 -24
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
2.42.0-beta.
|
|
1
|
+
2.42.0-beta.7
|
package/lib/math/market.d.ts
CHANGED
|
@@ -26,7 +26,7 @@ export declare function calculateAskPrice(market: PerpMarketAccount, oraclePrice
|
|
|
26
26
|
export declare function calculateNewMarketAfterTrade(baseAssetAmount: BN, direction: PositionDirection, market: PerpMarketAccount): PerpMarketAccount;
|
|
27
27
|
export declare function calculateOracleReserveSpread(market: PerpMarketAccount, oraclePriceData: OraclePriceData): BN;
|
|
28
28
|
export declare function calculateOracleSpread(price: BN, oraclePriceData: OraclePriceData): BN;
|
|
29
|
-
export declare function calculateMarketMarginRatio(market: PerpMarketAccount, size: BN, marginCategory: MarginCategory): number;
|
|
29
|
+
export declare function calculateMarketMarginRatio(market: PerpMarketAccount, size: BN, marginCategory: MarginCategory, customMarginRatio?: number): number;
|
|
30
30
|
export declare function calculateUnrealizedAssetWeight(market: PerpMarketAccount, quoteSpotMarket: SpotMarketAccount, unrealizedPnl: BN, marginCategory: MarginCategory, oraclePriceData: OraclePriceData): BN;
|
|
31
31
|
export declare function calculateMarketAvailablePNL(perpMarket: PerpMarketAccount, spotMarket: SpotMarketAccount): BN;
|
|
32
32
|
export declare function calculateMarketMaxAvailableInsurance(perpMarket: PerpMarketAccount, spotMarket: SpotMarketAccount): BN;
|
package/lib/math/market.js
CHANGED
|
@@ -60,11 +60,12 @@ function calculateOracleSpread(price, oraclePriceData) {
|
|
|
60
60
|
return price.sub(oraclePriceData.price);
|
|
61
61
|
}
|
|
62
62
|
exports.calculateOracleSpread = calculateOracleSpread;
|
|
63
|
-
function calculateMarketMarginRatio(market, size, marginCategory) {
|
|
63
|
+
function calculateMarketMarginRatio(market, size, marginCategory, customMarginRatio = 0) {
|
|
64
64
|
let marginRatio;
|
|
65
65
|
switch (marginCategory) {
|
|
66
66
|
case 'Initial': {
|
|
67
|
-
|
|
67
|
+
// use lowest leverage between max allowed and optional user custom max
|
|
68
|
+
marginRatio = Math.max((0, margin_1.calculateSizePremiumLiabilityWeight)(size, new anchor_1.BN(market.imfFactor), new anchor_1.BN(market.marginRatioInitial), numericConstants_1.MARGIN_PRECISION).toNumber(), customMarginRatio);
|
|
68
69
|
break;
|
|
69
70
|
}
|
|
70
71
|
case 'Maintenance': {
|
package/lib/math/spotMarket.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { BN } from '@coral-xyz/anchor';
|
|
2
2
|
import { MarginCategory, SpotBalanceType, SpotMarketAccount } from '../types';
|
|
3
3
|
export declare function castNumberToSpotPrecision(value: number | BN, spotMarket: SpotMarketAccount): BN;
|
|
4
|
-
export declare function calculateSpotMarketMarginRatio(market: SpotMarketAccount, oraclePrice: BN, marginCategory: MarginCategory, size: BN, balanceType: SpotBalanceType): number;
|
|
4
|
+
export declare function calculateSpotMarketMarginRatio(market: SpotMarketAccount, oraclePrice: BN, marginCategory: MarginCategory, size: BN, balanceType: SpotBalanceType, customMarginRatio?: number): number;
|
package/lib/math/spotMarket.js
CHANGED
|
@@ -14,14 +14,20 @@ function castNumberToSpotPrecision(value, spotMarket) {
|
|
|
14
14
|
}
|
|
15
15
|
}
|
|
16
16
|
exports.castNumberToSpotPrecision = castNumberToSpotPrecision;
|
|
17
|
-
function calculateSpotMarketMarginRatio(market, oraclePrice, marginCategory, size, balanceType) {
|
|
17
|
+
function calculateSpotMarketMarginRatio(market, oraclePrice, marginCategory, size, balanceType, customMarginRatio = 0) {
|
|
18
|
+
let marginRatio;
|
|
18
19
|
if ((0, types_1.isVariant)(balanceType, 'deposit')) {
|
|
19
20
|
const assetWeight = (0, spotBalance_1.calculateAssetWeight)(size, oraclePrice, market, marginCategory);
|
|
20
|
-
|
|
21
|
+
marginRatio = numericConstants_1.MARGIN_PRECISION.sub(assetWeight).toNumber();
|
|
21
22
|
}
|
|
22
23
|
else {
|
|
23
24
|
const liabilityWeight = (0, spotBalance_1.calculateLiabilityWeight)(size, market, marginCategory);
|
|
24
|
-
|
|
25
|
+
marginRatio = liabilityWeight.sub(numericConstants_1.MARGIN_PRECISION).toNumber();
|
|
25
26
|
}
|
|
27
|
+
if (marginCategory === 'Initial') {
|
|
28
|
+
// use lowest leverage between max allowed and optional user custom max
|
|
29
|
+
return Math.max(marginRatio, customMarginRatio);
|
|
30
|
+
}
|
|
31
|
+
return marginRatio;
|
|
26
32
|
}
|
|
27
33
|
exports.calculateSpotMarketMarginRatio = calculateSpotMarketMarginRatio;
|
|
@@ -10,9 +10,9 @@ export type OrderFillSimulation = {
|
|
|
10
10
|
weightedTokenValue: BN;
|
|
11
11
|
freeCollateralContribution: any;
|
|
12
12
|
};
|
|
13
|
-
export declare function getWorstCaseTokenAmounts(spotPosition: SpotPosition, spotMarketAccount: SpotMarketAccount, strictOraclePrice: StrictOraclePrice, marginCategory: MarginCategory): OrderFillSimulation;
|
|
14
|
-
export declare function calculateWeightedTokenValue(tokenAmount: BN, tokenValue: BN, oraclePrice: BN, spotMarket: SpotMarketAccount, marginCategory: MarginCategory): {
|
|
13
|
+
export declare function getWorstCaseTokenAmounts(spotPosition: SpotPosition, spotMarketAccount: SpotMarketAccount, strictOraclePrice: StrictOraclePrice, marginCategory: MarginCategory, customMarginRatio?: number): OrderFillSimulation;
|
|
14
|
+
export declare function calculateWeightedTokenValue(tokenAmount: BN, tokenValue: BN, oraclePrice: BN, spotMarket: SpotMarketAccount, marginCategory: MarginCategory, customMarginRatio?: number): {
|
|
15
15
|
weight: BN;
|
|
16
16
|
weightedTokenValue: BN;
|
|
17
17
|
};
|
|
18
|
-
export declare function simulateOrderFill(tokenAmount: BN, tokenValue: BN, openOrders: BN, strictOraclePrice: StrictOraclePrice, spotMarket: SpotMarketAccount, marginCategory: MarginCategory): OrderFillSimulation;
|
|
18
|
+
export declare function simulateOrderFill(tokenAmount: BN, tokenValue: BN, openOrders: BN, strictOraclePrice: StrictOraclePrice, spotMarket: SpotMarketAccount, marginCategory: MarginCategory, customMarginRatio?: number): OrderFillSimulation;
|
package/lib/math/spotPosition.js
CHANGED
|
@@ -2,16 +2,17 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.simulateOrderFill = exports.calculateWeightedTokenValue = exports.getWorstCaseTokenAmounts = exports.isSpotPositionAvailable = void 0;
|
|
4
4
|
const numericConstants_1 = require("../constants/numericConstants");
|
|
5
|
+
const anchor_1 = require("@coral-xyz/anchor");
|
|
5
6
|
const spotBalance_1 = require("./spotBalance");
|
|
6
7
|
function isSpotPositionAvailable(position) {
|
|
7
8
|
return position.scaledBalance.eq(numericConstants_1.ZERO) && position.openOrders === 0;
|
|
8
9
|
}
|
|
9
10
|
exports.isSpotPositionAvailable = isSpotPositionAvailable;
|
|
10
|
-
function getWorstCaseTokenAmounts(spotPosition, spotMarketAccount, strictOraclePrice, marginCategory) {
|
|
11
|
+
function getWorstCaseTokenAmounts(spotPosition, spotMarketAccount, strictOraclePrice, marginCategory, customMarginRatio) {
|
|
11
12
|
const tokenAmount = (0, spotBalance_1.getSignedTokenAmount)((0, spotBalance_1.getTokenAmount)(spotPosition.scaledBalance, spotMarketAccount, spotPosition.balanceType), spotPosition.balanceType);
|
|
12
13
|
const tokenValue = (0, spotBalance_1.getStrictTokenValue)(tokenAmount, spotMarketAccount.decimals, strictOraclePrice);
|
|
13
14
|
if (spotPosition.openBids.eq(numericConstants_1.ZERO) && spotPosition.openAsks.eq(numericConstants_1.ZERO)) {
|
|
14
|
-
const { weight, weightedTokenValue } = calculateWeightedTokenValue(tokenAmount, tokenValue, strictOraclePrice.current, spotMarketAccount, marginCategory);
|
|
15
|
+
const { weight, weightedTokenValue } = calculateWeightedTokenValue(tokenAmount, tokenValue, strictOraclePrice.current, spotMarketAccount, marginCategory, customMarginRatio);
|
|
15
16
|
return {
|
|
16
17
|
tokenAmount,
|
|
17
18
|
ordersValue: numericConstants_1.ZERO,
|
|
@@ -21,8 +22,8 @@ function getWorstCaseTokenAmounts(spotPosition, spotMarketAccount, strictOracleP
|
|
|
21
22
|
freeCollateralContribution: weightedTokenValue,
|
|
22
23
|
};
|
|
23
24
|
}
|
|
24
|
-
const bidsSimulation = simulateOrderFill(tokenAmount, tokenValue, spotPosition.openBids, strictOraclePrice, spotMarketAccount, marginCategory);
|
|
25
|
-
const asksSimulation = simulateOrderFill(tokenAmount, tokenValue, spotPosition.openAsks, strictOraclePrice, spotMarketAccount, marginCategory);
|
|
25
|
+
const bidsSimulation = simulateOrderFill(tokenAmount, tokenValue, spotPosition.openBids, strictOraclePrice, spotMarketAccount, marginCategory, customMarginRatio);
|
|
26
|
+
const asksSimulation = simulateOrderFill(tokenAmount, tokenValue, spotPosition.openAsks, strictOraclePrice, spotMarketAccount, marginCategory, customMarginRatio);
|
|
26
27
|
if (asksSimulation.freeCollateralContribution.lt(bidsSimulation.freeCollateralContribution)) {
|
|
27
28
|
return asksSimulation;
|
|
28
29
|
}
|
|
@@ -31,7 +32,7 @@ function getWorstCaseTokenAmounts(spotPosition, spotMarketAccount, strictOracleP
|
|
|
31
32
|
}
|
|
32
33
|
}
|
|
33
34
|
exports.getWorstCaseTokenAmounts = getWorstCaseTokenAmounts;
|
|
34
|
-
function calculateWeightedTokenValue(tokenAmount, tokenValue, oraclePrice, spotMarket, marginCategory) {
|
|
35
|
+
function calculateWeightedTokenValue(tokenAmount, tokenValue, oraclePrice, spotMarket, marginCategory, customMarginRatio) {
|
|
35
36
|
let weight;
|
|
36
37
|
if (tokenValue.gte(numericConstants_1.ZERO)) {
|
|
37
38
|
weight = (0, spotBalance_1.calculateAssetWeight)(tokenAmount, oraclePrice, spotMarket, marginCategory);
|
|
@@ -39,6 +40,16 @@ function calculateWeightedTokenValue(tokenAmount, tokenValue, oraclePrice, spotM
|
|
|
39
40
|
else {
|
|
40
41
|
weight = (0, spotBalance_1.calculateLiabilityWeight)(tokenAmount.abs(), spotMarket, marginCategory);
|
|
41
42
|
}
|
|
43
|
+
if (marginCategory === 'Initial' &&
|
|
44
|
+
customMarginRatio &&
|
|
45
|
+
spotMarket.marketIndex !== numericConstants_1.QUOTE_SPOT_MARKET_INDEX) {
|
|
46
|
+
const userCustomAssetWeight = tokenValue.gte(numericConstants_1.ZERO)
|
|
47
|
+
? anchor_1.BN.max(numericConstants_1.ZERO, numericConstants_1.SPOT_MARKET_WEIGHT_PRECISION.subn(customMarginRatio))
|
|
48
|
+
: numericConstants_1.SPOT_MARKET_WEIGHT_PRECISION.addn(customMarginRatio);
|
|
49
|
+
weight = tokenValue.gte(numericConstants_1.ZERO)
|
|
50
|
+
? anchor_1.BN.min(weight, userCustomAssetWeight)
|
|
51
|
+
: anchor_1.BN.max(weight, userCustomAssetWeight);
|
|
52
|
+
}
|
|
42
53
|
return {
|
|
43
54
|
weight: weight,
|
|
44
55
|
weightedTokenValue: tokenValue
|
|
@@ -47,13 +58,13 @@ function calculateWeightedTokenValue(tokenAmount, tokenValue, oraclePrice, spotM
|
|
|
47
58
|
};
|
|
48
59
|
}
|
|
49
60
|
exports.calculateWeightedTokenValue = calculateWeightedTokenValue;
|
|
50
|
-
function simulateOrderFill(tokenAmount, tokenValue, openOrders, strictOraclePrice, spotMarket, marginCategory) {
|
|
61
|
+
function simulateOrderFill(tokenAmount, tokenValue, openOrders, strictOraclePrice, spotMarket, marginCategory, customMarginRatio) {
|
|
51
62
|
const ordersValue = (0, spotBalance_1.getTokenValue)(openOrders.neg(), spotMarket.decimals, {
|
|
52
63
|
price: strictOraclePrice.max(),
|
|
53
64
|
});
|
|
54
65
|
const tokenAmountAfterFill = tokenAmount.add(openOrders);
|
|
55
66
|
const tokenValueAfterFill = tokenValue.add(ordersValue.neg());
|
|
56
|
-
const { weight, weightedTokenValue: weightedTokenValueAfterFill } = calculateWeightedTokenValue(tokenAmountAfterFill, tokenValueAfterFill, strictOraclePrice.current, spotMarket, marginCategory);
|
|
67
|
+
const { weight, weightedTokenValue: weightedTokenValueAfterFill } = calculateWeightedTokenValue(tokenAmountAfterFill, tokenValueAfterFill, strictOraclePrice.current, spotMarket, marginCategory, customMarginRatio);
|
|
57
68
|
const freeCollateralContribution = weightedTokenValueAfterFill.add(ordersValue);
|
|
58
69
|
return {
|
|
59
70
|
tokenAmount: tokenAmountAfterFill,
|
package/lib/user.d.ts
CHANGED
|
@@ -187,7 +187,7 @@ export declare class User {
|
|
|
187
187
|
spotAssetValue: BN;
|
|
188
188
|
spotLiabilityValue: BN;
|
|
189
189
|
}): BN;
|
|
190
|
-
getLeverageComponents(includeOpenOrders?: boolean): {
|
|
190
|
+
getLeverageComponents(includeOpenOrders?: boolean, marginCategory?: MarginCategory): {
|
|
191
191
|
perpLiabilityValue: BN;
|
|
192
192
|
perpPnl: BN;
|
|
193
193
|
spotAssetValue: BN;
|
package/lib/user.js
CHANGED
|
@@ -411,7 +411,7 @@ class User {
|
|
|
411
411
|
return this.getPerpBuyingPowerFromFreeCollateralAndBaseAssetAmount(marketIndex, freeCollateral, worstCaseBaseAssetAmount);
|
|
412
412
|
}
|
|
413
413
|
getPerpBuyingPowerFromFreeCollateralAndBaseAssetAmount(marketIndex, freeCollateral, baseAssetAmount) {
|
|
414
|
-
const marginRatio = (0, _1.calculateMarketMarginRatio)(this.driftClient.getPerpMarketAccount(marketIndex), baseAssetAmount, 'Initial');
|
|
414
|
+
const marginRatio = (0, _1.calculateMarketMarginRatio)(this.driftClient.getPerpMarketAccount(marketIndex), baseAssetAmount, 'Initial', this.getUserAccount().maxMarginRatio);
|
|
415
415
|
return freeCollateral.mul(numericConstants_1.MARGIN_PRECISION).div(new _1.BN(marginRatio));
|
|
416
416
|
}
|
|
417
417
|
/**
|
|
@@ -577,7 +577,7 @@ class User {
|
|
|
577
577
|
continue;
|
|
578
578
|
}
|
|
579
579
|
}
|
|
580
|
-
const { tokenAmount: worstCaseTokenAmount, ordersValue: worstCaseQuoteTokenAmount, } = (0, spotPosition_1.getWorstCaseTokenAmounts)(spotPosition, spotMarketAccount, strictOraclePrice, marginCategory
|
|
580
|
+
const { tokenAmount: worstCaseTokenAmount, ordersValue: worstCaseQuoteTokenAmount, } = (0, spotPosition_1.getWorstCaseTokenAmounts)(spotPosition, spotMarketAccount, strictOraclePrice, marginCategory, this.getUserAccount().maxMarginRatio);
|
|
581
581
|
if (worstCaseTokenAmount.gt(numericConstants_1.ZERO) && countForBase) {
|
|
582
582
|
const baseAssetValue = this.getSpotAssetValue(worstCaseTokenAmount, strictOraclePrice, spotMarketAccount, marginCategory);
|
|
583
583
|
totalAssetValue = totalAssetValue.add(baseAssetValue);
|
|
@@ -620,8 +620,9 @@ class User {
|
|
|
620
620
|
let liabilityValue = (0, _1.getStrictTokenValue)(tokenAmount, spotMarketAccount.decimals, strictOraclePrice);
|
|
621
621
|
if (marginCategory !== undefined) {
|
|
622
622
|
let weight = (0, spotBalance_1.calculateLiabilityWeight)(tokenAmount, spotMarketAccount, marginCategory);
|
|
623
|
-
if (marginCategory === 'Initial'
|
|
624
|
-
|
|
623
|
+
if (marginCategory === 'Initial' &&
|
|
624
|
+
spotMarketAccount.marketIndex !== numericConstants_1.QUOTE_SPOT_MARKET_INDEX) {
|
|
625
|
+
weight = _1.BN.max(weight, numericConstants_1.SPOT_MARKET_WEIGHT_PRECISION.addn(this.getUserAccount().maxMarginRatio));
|
|
625
626
|
}
|
|
626
627
|
if (liquidationBuffer !== undefined) {
|
|
627
628
|
weight = weight.add(liquidationBuffer);
|
|
@@ -639,7 +640,12 @@ class User {
|
|
|
639
640
|
getSpotAssetValue(tokenAmount, strictOraclePrice, spotMarketAccount, marginCategory) {
|
|
640
641
|
let assetValue = (0, _1.getStrictTokenValue)(tokenAmount, spotMarketAccount.decimals, strictOraclePrice);
|
|
641
642
|
if (marginCategory !== undefined) {
|
|
642
|
-
|
|
643
|
+
let weight = (0, spotBalance_1.calculateAssetWeight)(tokenAmount, strictOraclePrice.current, spotMarketAccount, marginCategory);
|
|
644
|
+
if (marginCategory === 'Initial' &&
|
|
645
|
+
spotMarketAccount.marketIndex !== numericConstants_1.QUOTE_SPOT_MARKET_INDEX) {
|
|
646
|
+
const userCustomAssetWeight = _1.BN.max(numericConstants_1.ZERO, numericConstants_1.SPOT_MARKET_WEIGHT_PRECISION.subn(this.getUserAccount().maxMarginRatio));
|
|
647
|
+
weight = _1.BN.min(weight, userCustomAssetWeight);
|
|
648
|
+
}
|
|
643
649
|
assetValue = assetValue.mul(weight).div(numericConstants_1.SPOT_MARKET_WEIGHT_PRECISION);
|
|
644
650
|
}
|
|
645
651
|
return assetValue;
|
|
@@ -702,10 +708,7 @@ class User {
|
|
|
702
708
|
.mul(valuationPrice)
|
|
703
709
|
.div(numericConstants_1.BASE_PRECISION);
|
|
704
710
|
if (marginCategory) {
|
|
705
|
-
let marginRatio = new _1.BN((0, _1.calculateMarketMarginRatio)(market, baseAssetAmount.abs(), marginCategory));
|
|
706
|
-
if (marginCategory === 'Initial') {
|
|
707
|
-
marginRatio = _1.BN.max(marginRatio, new _1.BN(this.getUserAccount().maxMarginRatio));
|
|
708
|
-
}
|
|
711
|
+
let marginRatio = new _1.BN((0, _1.calculateMarketMarginRatio)(market, baseAssetAmount.abs(), marginCategory, this.getUserAccount().maxMarginRatio));
|
|
709
712
|
if (liquidationBuffer !== undefined) {
|
|
710
713
|
marginRatio = marginRatio.add(liquidationBuffer);
|
|
711
714
|
}
|
|
@@ -834,10 +837,10 @@ class User {
|
|
|
834
837
|
}
|
|
835
838
|
return totalLiabilityValue.mul(numericConstants_1.TEN_THOUSAND).div(netAssetValue);
|
|
836
839
|
}
|
|
837
|
-
getLeverageComponents(includeOpenOrders = true) {
|
|
838
|
-
const perpLiability = this.getTotalPerpPositionValue(
|
|
839
|
-
const perpPnl = this.getUnrealizedPNL(true);
|
|
840
|
-
const { totalAssetValue: spotAssetValue, totalLiabilityValue: spotLiabilityValue, } = this.getSpotMarketAssetAndLiabilityValue(undefined,
|
|
840
|
+
getLeverageComponents(includeOpenOrders = true, marginCategory = undefined) {
|
|
841
|
+
const perpLiability = this.getTotalPerpPositionValue(marginCategory, undefined, includeOpenOrders);
|
|
842
|
+
const perpPnl = this.getUnrealizedPNL(true, undefined, marginCategory);
|
|
843
|
+
const { totalAssetValue: spotAssetValue, totalLiabilityValue: spotLiabilityValue, } = this.getSpotMarketAssetAndLiabilityValue(undefined, marginCategory, undefined, includeOpenOrders);
|
|
841
844
|
return {
|
|
842
845
|
perpLiabilityValue: perpLiability,
|
|
843
846
|
perpPnl,
|
|
@@ -889,7 +892,7 @@ class User {
|
|
|
889
892
|
let rawMarginRatio;
|
|
890
893
|
switch (marginCategory) {
|
|
891
894
|
case 'Initial':
|
|
892
|
-
rawMarginRatio = market.marginRatioInitial;
|
|
895
|
+
rawMarginRatio = Math.max(market.marginRatioInitial, this.getUserAccount().maxMarginRatio);
|
|
893
896
|
break;
|
|
894
897
|
case 'Maintenance':
|
|
895
898
|
rawMarginRatio = market.marginRatioMaintenance;
|
|
@@ -905,7 +908,7 @@ class User {
|
|
|
905
908
|
.mul(numericConstants_1.PRICE_PRECISION)
|
|
906
909
|
.div(marketPrice));
|
|
907
910
|
// margin ratio incorporting upper bound on size
|
|
908
|
-
let marginRatio = (0, _1.calculateMarketMarginRatio)(market, maxSize, marginCategory);
|
|
911
|
+
let marginRatio = (0, _1.calculateMarketMarginRatio)(market, maxSize, marginCategory, this.getUserAccount().maxMarginRatio);
|
|
909
912
|
// use more fesible size since imf factor activated
|
|
910
913
|
let attempts = 0;
|
|
911
914
|
while (marginRatio > rawMarginRatio + 1e-4 && attempts < 10) {
|
|
@@ -916,7 +919,7 @@ class User {
|
|
|
916
919
|
.mul(numericConstants_1.PRICE_PRECISION)
|
|
917
920
|
.div(marketPrice));
|
|
918
921
|
// margin ratio incorporting more fesible target size
|
|
919
|
-
marginRatio = (0, _1.calculateMarketMarginRatio)(market, targetSize, marginCategory);
|
|
922
|
+
marginRatio = (0, _1.calculateMarketMarginRatio)(market, targetSize, marginCategory, this.getUserAccount().maxMarginRatio);
|
|
920
923
|
attempts += 1;
|
|
921
924
|
}
|
|
922
925
|
// how much more liabilities can be opened w remaining free collateral
|
|
@@ -1277,19 +1280,19 @@ class User {
|
|
|
1277
1280
|
let freeCollateral = this.getFreeCollateral();
|
|
1278
1281
|
const marginRatio = (0, _1.calculateSpotMarketMarginRatio)(market, oraclePrice, 'Initial', numericConstants_1.ZERO, (0, types_1.isVariant)(direction, 'long')
|
|
1279
1282
|
? _1.SpotBalanceType.DEPOSIT
|
|
1280
|
-
: _1.SpotBalanceType.BORROW);
|
|
1283
|
+
: _1.SpotBalanceType.BORROW, this.getUserAccount().maxMarginRatio);
|
|
1281
1284
|
let tradeAmount = numericConstants_1.ZERO;
|
|
1282
1285
|
if (this.getUserAccount().isMarginTradingEnabled) {
|
|
1283
1286
|
// if the user is buying/selling and already short/long, need to account for closing out short/long
|
|
1284
1287
|
if ((0, types_1.isVariant)(direction, 'long') && currentSpotMarketNetValue.lt(numericConstants_1.ZERO)) {
|
|
1285
1288
|
tradeAmount = currentSpotMarketNetValue.abs();
|
|
1286
|
-
const marginRatio = (0, _1.calculateSpotMarketMarginRatio)(market, oraclePrice, 'Initial', this.getTokenAmount(targetMarketIndex).abs(), _1.SpotBalanceType.BORROW);
|
|
1289
|
+
const marginRatio = (0, _1.calculateSpotMarketMarginRatio)(market, oraclePrice, 'Initial', this.getTokenAmount(targetMarketIndex).abs(), _1.SpotBalanceType.BORROW, this.getUserAccount().maxMarginRatio);
|
|
1287
1290
|
freeCollateral = freeCollateral.add(tradeAmount.mul(new _1.BN(marginRatio)).div(numericConstants_1.MARGIN_PRECISION));
|
|
1288
1291
|
}
|
|
1289
1292
|
else if ((0, types_1.isVariant)(direction, 'short') &&
|
|
1290
1293
|
currentSpotMarketNetValue.gt(numericConstants_1.ZERO)) {
|
|
1291
1294
|
tradeAmount = currentSpotMarketNetValue;
|
|
1292
|
-
const marginRatio = (0, _1.calculateSpotMarketMarginRatio)(market, oraclePrice, 'Initial', this.getTokenAmount(targetMarketIndex), _1.SpotBalanceType.DEPOSIT);
|
|
1295
|
+
const marginRatio = (0, _1.calculateSpotMarketMarginRatio)(market, oraclePrice, 'Initial', this.getTokenAmount(targetMarketIndex), _1.SpotBalanceType.DEPOSIT, this.getUserAccount().maxMarginRatio);
|
|
1293
1296
|
freeCollateral = freeCollateral.add(tradeAmount.mul(new _1.BN(marginRatio)).div(numericConstants_1.MARGIN_PRECISION));
|
|
1294
1297
|
}
|
|
1295
1298
|
tradeAmount = tradeAmount.add(freeCollateral.mul(numericConstants_1.MARGIN_PRECISION).div(new _1.BN(marginRatio)));
|
|
@@ -1332,7 +1335,7 @@ class User {
|
|
|
1332
1335
|
const outContributionInitial = this.calculateSpotPositionFreeCollateralContribution(outSpotPosition, outStrictOraclePrice);
|
|
1333
1336
|
const { totalAssetValue: outTotalAssetValueInitial, totalLiabilityValue: outTotalLiabilityValueInitial, } = this.calculateSpotPositionLeverageContribution(outSpotPosition, outStrictOraclePrice);
|
|
1334
1337
|
const initialContribution = inContributionInitial.add(outContributionInitial);
|
|
1335
|
-
const { perpLiabilityValue, perpPnl, spotAssetValue, spotLiabilityValue } = this.getLeverageComponents();
|
|
1338
|
+
const { perpLiabilityValue, perpPnl, spotAssetValue, spotLiabilityValue } = this.getLeverageComponents(undefined, 'Initial');
|
|
1336
1339
|
if (!calculateSwap) {
|
|
1337
1340
|
calculateSwap = (inSwap) => {
|
|
1338
1341
|
return inSwap
|
|
@@ -1440,14 +1443,14 @@ class User {
|
|
|
1440
1443
|
calculateSpotPositionFreeCollateralContribution(spotPosition, strictOraclePrice) {
|
|
1441
1444
|
const marginCategory = 'Initial';
|
|
1442
1445
|
const spotMarketAccount = this.driftClient.getSpotMarketAccount(spotPosition.marketIndex);
|
|
1443
|
-
const { freeCollateralContribution } = (0, spotPosition_1.getWorstCaseTokenAmounts)(spotPosition, spotMarketAccount, strictOraclePrice, marginCategory);
|
|
1446
|
+
const { freeCollateralContribution } = (0, spotPosition_1.getWorstCaseTokenAmounts)(spotPosition, spotMarketAccount, strictOraclePrice, marginCategory, this.getUserAccount().maxMarginRatio);
|
|
1444
1447
|
return freeCollateralContribution;
|
|
1445
1448
|
}
|
|
1446
1449
|
calculateSpotPositionLeverageContribution(spotPosition, strictOraclePrice) {
|
|
1447
1450
|
let totalAssetValue = numericConstants_1.ZERO;
|
|
1448
1451
|
let totalLiabilityValue = numericConstants_1.ZERO;
|
|
1449
1452
|
const spotMarketAccount = this.driftClient.getSpotMarketAccount(spotPosition.marketIndex);
|
|
1450
|
-
const { tokenValue, ordersValue } = (0, spotPosition_1.getWorstCaseTokenAmounts)(spotPosition, spotMarketAccount, strictOraclePrice, 'Initial');
|
|
1453
|
+
const { tokenValue, ordersValue } = (0, spotPosition_1.getWorstCaseTokenAmounts)(spotPosition, spotMarketAccount, strictOraclePrice, 'Initial', this.getUserAccount().maxMarginRatio);
|
|
1451
1454
|
if (tokenValue.gte(numericConstants_1.ZERO)) {
|
|
1452
1455
|
totalAssetValue = tokenValue;
|
|
1453
1456
|
}
|
|
@@ -1487,7 +1490,7 @@ class User {
|
|
|
1487
1490
|
this.getEmptySpotPosition(outMarketIndex);
|
|
1488
1491
|
const { totalAssetValue: inTotalAssetValueInitial, totalLiabilityValue: inTotalLiabilityValueInitial, } = this.calculateSpotPositionLeverageContribution(inSpotPosition, inStrictOraclePrice);
|
|
1489
1492
|
const { totalAssetValue: outTotalAssetValueInitial, totalLiabilityValue: outTotalLiabilityValueInitial, } = this.calculateSpotPositionLeverageContribution(outSpotPosition, outStrictOraclePrice);
|
|
1490
|
-
const { perpLiabilityValue, perpPnl, spotAssetValue, spotLiabilityValue } = this.getLeverageComponents();
|
|
1493
|
+
const { perpLiabilityValue, perpPnl, spotAssetValue, spotLiabilityValue } = this.getLeverageComponents(undefined, 'Initial');
|
|
1491
1494
|
const inPositionAfter = this.cloneAndUpdateSpotPosition(inSpotPosition, inAmount.abs().neg(), inMarket);
|
|
1492
1495
|
const outPositionAfter = this.cloneAndUpdateSpotPosition(outSpotPosition, outAmount.abs(), outMarket);
|
|
1493
1496
|
const { totalAssetValue: inTotalAssetValueAfter, totalLiabilityValue: inTotalLiabilityValueAfter, } = this.calculateSpotPositionLeverageContribution(inPositionAfter, inStrictOraclePrice);
|
|
@@ -1801,7 +1804,7 @@ class User {
|
|
|
1801
1804
|
const oraclePriceData = this.driftClient.getOraclePriceDataAndSlot(perpMarket.amm.oracle).data;
|
|
1802
1805
|
const oraclePrice = oraclePriceData.price;
|
|
1803
1806
|
const worstCaseBaseAmount = (0, margin_1.calculateWorstCaseBaseAssetAmount)(perpPosition);
|
|
1804
|
-
const marginRatio = new _1.BN((0, _1.calculateMarketMarginRatio)(perpMarket, worstCaseBaseAmount.abs(), marginCategory));
|
|
1807
|
+
const marginRatio = new _1.BN((0, _1.calculateMarketMarginRatio)(perpMarket, worstCaseBaseAmount.abs(), marginCategory, this.getUserAccount().maxMarginRatio));
|
|
1805
1808
|
const quoteSpotMarket = this.driftClient.getSpotMarketAccount(perpMarket.quoteSpotMarketIndex);
|
|
1806
1809
|
const quoteOraclePriceData = this.driftClient.getOraclePriceDataAndSlot(quoteSpotMarket.oracle).data;
|
|
1807
1810
|
const baseAssetValue = worstCaseBaseAmount
|
|
@@ -1861,7 +1864,7 @@ class User {
|
|
|
1861
1864
|
netQuoteValue = netQuoteValue.add(tokenAmount);
|
|
1862
1865
|
continue;
|
|
1863
1866
|
}
|
|
1864
|
-
const { tokenAmount: worstCaseTokenAmount, tokenValue: tokenValue, weight, weightedTokenValue: weightedTokenValue, ordersValue: ordersValue, } = (0, spotPosition_1.getWorstCaseTokenAmounts)(spotPosition, spotMarketAccount, strictOraclePrice, marginCategory);
|
|
1867
|
+
const { tokenAmount: worstCaseTokenAmount, tokenValue: tokenValue, weight, weightedTokenValue: weightedTokenValue, ordersValue: ordersValue, } = (0, spotPosition_1.getWorstCaseTokenAmounts)(spotPosition, spotMarketAccount, strictOraclePrice, marginCategory, this.getUserAccount().maxMarginRatio);
|
|
1865
1868
|
netQuoteValue = netQuoteValue.add(ordersValue);
|
|
1866
1869
|
const baseAssetValue = tokenValue.abs();
|
|
1867
1870
|
const weightedValue = weightedTokenValue.abs();
|
|
@@ -1888,7 +1891,7 @@ class User {
|
|
|
1888
1891
|
const spotMarketAccount = this.driftClient.getQuoteSpotMarketAccount();
|
|
1889
1892
|
const oraclePriceData = this.getOracleDataForSpotMarket(numericConstants_1.QUOTE_SPOT_MARKET_INDEX);
|
|
1890
1893
|
const baseAssetValue = (0, _1.getTokenValue)(netQuoteValue, spotMarketAccount.decimals, oraclePriceData);
|
|
1891
|
-
const { weight, weightedTokenValue } = (0, spotPosition_1.calculateWeightedTokenValue)(netQuoteValue, baseAssetValue, oraclePriceData.price, spotMarketAccount, marginCategory);
|
|
1894
|
+
const { weight, weightedTokenValue } = (0, spotPosition_1.calculateWeightedTokenValue)(netQuoteValue, baseAssetValue, oraclePriceData.price, spotMarketAccount, marginCategory, this.getUserAccount().maxMarginRatio);
|
|
1892
1895
|
if (netQuoteValue.lt(numericConstants_1.ZERO)) {
|
|
1893
1896
|
healthComponents.borrows.push({
|
|
1894
1897
|
marketIndex: spotMarketAccount.marketIndex,
|
package/package.json
CHANGED
package/src/math/market.ts
CHANGED
|
@@ -129,17 +129,22 @@ export function calculateOracleSpread(
|
|
|
129
129
|
export function calculateMarketMarginRatio(
|
|
130
130
|
market: PerpMarketAccount,
|
|
131
131
|
size: BN,
|
|
132
|
-
marginCategory: MarginCategory
|
|
132
|
+
marginCategory: MarginCategory,
|
|
133
|
+
customMarginRatio = 0
|
|
133
134
|
): number {
|
|
134
135
|
let marginRatio;
|
|
135
136
|
switch (marginCategory) {
|
|
136
137
|
case 'Initial': {
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
138
|
+
// use lowest leverage between max allowed and optional user custom max
|
|
139
|
+
marginRatio = Math.max(
|
|
140
|
+
calculateSizePremiumLiabilityWeight(
|
|
141
|
+
size,
|
|
142
|
+
new BN(market.imfFactor),
|
|
143
|
+
new BN(market.marginRatioInitial),
|
|
144
|
+
MARGIN_PRECISION
|
|
145
|
+
).toNumber(),
|
|
146
|
+
customMarginRatio
|
|
147
|
+
);
|
|
143
148
|
break;
|
|
144
149
|
}
|
|
145
150
|
case 'Maintenance': {
|
package/src/math/spotMarket.ts
CHANGED
|
@@ -24,8 +24,11 @@ export function calculateSpotMarketMarginRatio(
|
|
|
24
24
|
oraclePrice: BN,
|
|
25
25
|
marginCategory: MarginCategory,
|
|
26
26
|
size: BN,
|
|
27
|
-
balanceType: SpotBalanceType
|
|
27
|
+
balanceType: SpotBalanceType,
|
|
28
|
+
customMarginRatio = 0
|
|
28
29
|
): number {
|
|
30
|
+
let marginRatio;
|
|
31
|
+
|
|
29
32
|
if (isVariant(balanceType, 'deposit')) {
|
|
30
33
|
const assetWeight = calculateAssetWeight(
|
|
31
34
|
size,
|
|
@@ -33,13 +36,20 @@ export function calculateSpotMarketMarginRatio(
|
|
|
33
36
|
market,
|
|
34
37
|
marginCategory
|
|
35
38
|
);
|
|
36
|
-
|
|
39
|
+
marginRatio = MARGIN_PRECISION.sub(assetWeight).toNumber();
|
|
37
40
|
} else {
|
|
38
41
|
const liabilityWeight = calculateLiabilityWeight(
|
|
39
42
|
size,
|
|
40
43
|
market,
|
|
41
44
|
marginCategory
|
|
42
45
|
);
|
|
43
|
-
|
|
46
|
+
marginRatio = liabilityWeight.sub(MARGIN_PRECISION).toNumber();
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (marginCategory === 'Initial') {
|
|
50
|
+
// use lowest leverage between max allowed and optional user custom max
|
|
51
|
+
return Math.max(marginRatio, customMarginRatio);
|
|
44
52
|
}
|
|
53
|
+
|
|
54
|
+
return marginRatio;
|
|
45
55
|
}
|
package/src/math/spotPosition.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { MarginCategory, SpotMarketAccount, SpotPosition } from '../types';
|
|
2
2
|
import {
|
|
3
|
+
QUOTE_SPOT_MARKET_INDEX,
|
|
3
4
|
SPOT_MARKET_WEIGHT_PRECISION,
|
|
4
5
|
ZERO,
|
|
5
6
|
} from '../constants/numericConstants';
|
|
@@ -31,7 +32,8 @@ export function getWorstCaseTokenAmounts(
|
|
|
31
32
|
spotPosition: SpotPosition,
|
|
32
33
|
spotMarketAccount: SpotMarketAccount,
|
|
33
34
|
strictOraclePrice: StrictOraclePrice,
|
|
34
|
-
marginCategory: MarginCategory
|
|
35
|
+
marginCategory: MarginCategory,
|
|
36
|
+
customMarginRatio?: number
|
|
35
37
|
): OrderFillSimulation {
|
|
36
38
|
const tokenAmount = getSignedTokenAmount(
|
|
37
39
|
getTokenAmount(
|
|
@@ -54,7 +56,8 @@ export function getWorstCaseTokenAmounts(
|
|
|
54
56
|
tokenValue,
|
|
55
57
|
strictOraclePrice.current,
|
|
56
58
|
spotMarketAccount,
|
|
57
|
-
marginCategory
|
|
59
|
+
marginCategory,
|
|
60
|
+
customMarginRatio
|
|
58
61
|
);
|
|
59
62
|
return {
|
|
60
63
|
tokenAmount,
|
|
@@ -72,7 +75,8 @@ export function getWorstCaseTokenAmounts(
|
|
|
72
75
|
spotPosition.openBids,
|
|
73
76
|
strictOraclePrice,
|
|
74
77
|
spotMarketAccount,
|
|
75
|
-
marginCategory
|
|
78
|
+
marginCategory,
|
|
79
|
+
customMarginRatio
|
|
76
80
|
);
|
|
77
81
|
const asksSimulation = simulateOrderFill(
|
|
78
82
|
tokenAmount,
|
|
@@ -80,7 +84,8 @@ export function getWorstCaseTokenAmounts(
|
|
|
80
84
|
spotPosition.openAsks,
|
|
81
85
|
strictOraclePrice,
|
|
82
86
|
spotMarketAccount,
|
|
83
|
-
marginCategory
|
|
87
|
+
marginCategory,
|
|
88
|
+
customMarginRatio
|
|
84
89
|
);
|
|
85
90
|
|
|
86
91
|
if (
|
|
@@ -99,7 +104,8 @@ export function calculateWeightedTokenValue(
|
|
|
99
104
|
tokenValue: BN,
|
|
100
105
|
oraclePrice: BN,
|
|
101
106
|
spotMarket: SpotMarketAccount,
|
|
102
|
-
marginCategory: MarginCategory
|
|
107
|
+
marginCategory: MarginCategory,
|
|
108
|
+
customMarginRatio?: number
|
|
103
109
|
): { weight: BN; weightedTokenValue: BN } {
|
|
104
110
|
let weight: BN;
|
|
105
111
|
if (tokenValue.gte(ZERO)) {
|
|
@@ -117,6 +123,20 @@ export function calculateWeightedTokenValue(
|
|
|
117
123
|
);
|
|
118
124
|
}
|
|
119
125
|
|
|
126
|
+
if (
|
|
127
|
+
marginCategory === 'Initial' &&
|
|
128
|
+
customMarginRatio &&
|
|
129
|
+
spotMarket.marketIndex !== QUOTE_SPOT_MARKET_INDEX
|
|
130
|
+
) {
|
|
131
|
+
const userCustomAssetWeight = tokenValue.gte(ZERO)
|
|
132
|
+
? BN.max(ZERO, SPOT_MARKET_WEIGHT_PRECISION.subn(customMarginRatio))
|
|
133
|
+
: SPOT_MARKET_WEIGHT_PRECISION.addn(customMarginRatio);
|
|
134
|
+
|
|
135
|
+
weight = tokenValue.gte(ZERO)
|
|
136
|
+
? BN.min(weight, userCustomAssetWeight)
|
|
137
|
+
: BN.max(weight, userCustomAssetWeight);
|
|
138
|
+
}
|
|
139
|
+
|
|
120
140
|
return {
|
|
121
141
|
weight: weight,
|
|
122
142
|
weightedTokenValue: tokenValue
|
|
@@ -131,7 +151,8 @@ export function simulateOrderFill(
|
|
|
131
151
|
openOrders: BN,
|
|
132
152
|
strictOraclePrice: StrictOraclePrice,
|
|
133
153
|
spotMarket: SpotMarketAccount,
|
|
134
|
-
marginCategory: MarginCategory
|
|
154
|
+
marginCategory: MarginCategory,
|
|
155
|
+
customMarginRatio?: number
|
|
135
156
|
): OrderFillSimulation {
|
|
136
157
|
const ordersValue = getTokenValue(openOrders.neg(), spotMarket.decimals, {
|
|
137
158
|
price: strictOraclePrice.max(),
|
|
@@ -145,7 +166,8 @@ export function simulateOrderFill(
|
|
|
145
166
|
tokenValueAfterFill,
|
|
146
167
|
strictOraclePrice.current,
|
|
147
168
|
spotMarket,
|
|
148
|
-
marginCategory
|
|
169
|
+
marginCategory,
|
|
170
|
+
customMarginRatio
|
|
149
171
|
);
|
|
150
172
|
|
|
151
173
|
const freeCollateralContribution =
|
package/src/user.ts
CHANGED
|
@@ -78,7 +78,6 @@ import {
|
|
|
78
78
|
getWorstCaseTokenAmounts,
|
|
79
79
|
isSpotPositionAvailable,
|
|
80
80
|
} from './math/spotPosition';
|
|
81
|
-
|
|
82
81
|
import { calculateLiveOracleTwap } from './math/oracles';
|
|
83
82
|
import { getPerpMarketTierNumber, getSpotMarketTierNumber } from './math/tiers';
|
|
84
83
|
import { StrictOraclePrice } from './oracles/strictOraclePrice';
|
|
@@ -632,7 +631,8 @@ export class User {
|
|
|
632
631
|
const marginRatio = calculateMarketMarginRatio(
|
|
633
632
|
this.driftClient.getPerpMarketAccount(marketIndex),
|
|
634
633
|
baseAssetAmount,
|
|
635
|
-
'Initial'
|
|
634
|
+
'Initial',
|
|
635
|
+
this.getUserAccount().maxMarginRatio
|
|
636
636
|
);
|
|
637
637
|
|
|
638
638
|
return freeCollateral.mul(MARGIN_PRECISION).div(new BN(marginRatio));
|
|
@@ -968,7 +968,8 @@ export class User {
|
|
|
968
968
|
spotPosition,
|
|
969
969
|
spotMarketAccount,
|
|
970
970
|
strictOraclePrice,
|
|
971
|
-
marginCategory
|
|
971
|
+
marginCategory,
|
|
972
|
+
this.getUserAccount().maxMarginRatio
|
|
972
973
|
);
|
|
973
974
|
|
|
974
975
|
if (worstCaseTokenAmount.gt(ZERO) && countForBase) {
|
|
@@ -1067,8 +1068,16 @@ export class User {
|
|
|
1067
1068
|
marginCategory
|
|
1068
1069
|
);
|
|
1069
1070
|
|
|
1070
|
-
if (
|
|
1071
|
-
|
|
1071
|
+
if (
|
|
1072
|
+
marginCategory === 'Initial' &&
|
|
1073
|
+
spotMarketAccount.marketIndex !== QUOTE_SPOT_MARKET_INDEX
|
|
1074
|
+
) {
|
|
1075
|
+
weight = BN.max(
|
|
1076
|
+
weight,
|
|
1077
|
+
SPOT_MARKET_WEIGHT_PRECISION.addn(
|
|
1078
|
+
this.getUserAccount().maxMarginRatio
|
|
1079
|
+
)
|
|
1080
|
+
);
|
|
1072
1081
|
}
|
|
1073
1082
|
|
|
1074
1083
|
if (liquidationBuffer !== undefined) {
|
|
@@ -1114,13 +1123,26 @@ export class User {
|
|
|
1114
1123
|
);
|
|
1115
1124
|
|
|
1116
1125
|
if (marginCategory !== undefined) {
|
|
1117
|
-
|
|
1126
|
+
let weight = calculateAssetWeight(
|
|
1118
1127
|
tokenAmount,
|
|
1119
1128
|
strictOraclePrice.current,
|
|
1120
1129
|
spotMarketAccount,
|
|
1121
1130
|
marginCategory
|
|
1122
1131
|
);
|
|
1123
1132
|
|
|
1133
|
+
if (
|
|
1134
|
+
marginCategory === 'Initial' &&
|
|
1135
|
+
spotMarketAccount.marketIndex !== QUOTE_SPOT_MARKET_INDEX
|
|
1136
|
+
) {
|
|
1137
|
+
const userCustomAssetWeight = BN.max(
|
|
1138
|
+
ZERO,
|
|
1139
|
+
SPOT_MARKET_WEIGHT_PRECISION.subn(
|
|
1140
|
+
this.getUserAccount().maxMarginRatio
|
|
1141
|
+
)
|
|
1142
|
+
);
|
|
1143
|
+
weight = BN.min(weight, userCustomAssetWeight);
|
|
1144
|
+
}
|
|
1145
|
+
|
|
1124
1146
|
assetValue = assetValue.mul(weight).div(SPOT_MARKET_WEIGHT_PRECISION);
|
|
1125
1147
|
}
|
|
1126
1148
|
|
|
@@ -1254,17 +1276,11 @@ export class User {
|
|
|
1254
1276
|
calculateMarketMarginRatio(
|
|
1255
1277
|
market,
|
|
1256
1278
|
baseAssetAmount.abs(),
|
|
1257
|
-
marginCategory
|
|
1279
|
+
marginCategory,
|
|
1280
|
+
this.getUserAccount().maxMarginRatio
|
|
1258
1281
|
)
|
|
1259
1282
|
);
|
|
1260
1283
|
|
|
1261
|
-
if (marginCategory === 'Initial') {
|
|
1262
|
-
marginRatio = BN.max(
|
|
1263
|
-
marginRatio,
|
|
1264
|
-
new BN(this.getUserAccount().maxMarginRatio)
|
|
1265
|
-
);
|
|
1266
|
-
}
|
|
1267
|
-
|
|
1268
1284
|
if (liquidationBuffer !== undefined) {
|
|
1269
1285
|
marginRatio = marginRatio.add(liquidationBuffer);
|
|
1270
1286
|
}
|
|
@@ -1494,25 +1510,28 @@ export class User {
|
|
|
1494
1510
|
return totalLiabilityValue.mul(TEN_THOUSAND).div(netAssetValue);
|
|
1495
1511
|
}
|
|
1496
1512
|
|
|
1497
|
-
getLeverageComponents(
|
|
1513
|
+
getLeverageComponents(
|
|
1514
|
+
includeOpenOrders = true,
|
|
1515
|
+
marginCategory: MarginCategory = undefined
|
|
1516
|
+
): {
|
|
1498
1517
|
perpLiabilityValue: BN;
|
|
1499
1518
|
perpPnl: BN;
|
|
1500
1519
|
spotAssetValue: BN;
|
|
1501
1520
|
spotLiabilityValue: BN;
|
|
1502
1521
|
} {
|
|
1503
1522
|
const perpLiability = this.getTotalPerpPositionValue(
|
|
1504
|
-
|
|
1523
|
+
marginCategory,
|
|
1505
1524
|
undefined,
|
|
1506
1525
|
includeOpenOrders
|
|
1507
1526
|
);
|
|
1508
|
-
const perpPnl = this.getUnrealizedPNL(true);
|
|
1527
|
+
const perpPnl = this.getUnrealizedPNL(true, undefined, marginCategory);
|
|
1509
1528
|
|
|
1510
1529
|
const {
|
|
1511
1530
|
totalAssetValue: spotAssetValue,
|
|
1512
1531
|
totalLiabilityValue: spotLiabilityValue,
|
|
1513
1532
|
} = this.getSpotMarketAssetAndLiabilityValue(
|
|
1514
1533
|
undefined,
|
|
1515
|
-
|
|
1534
|
+
marginCategory,
|
|
1516
1535
|
undefined,
|
|
1517
1536
|
includeOpenOrders
|
|
1518
1537
|
);
|
|
@@ -1599,7 +1618,10 @@ export class User {
|
|
|
1599
1618
|
|
|
1600
1619
|
switch (marginCategory) {
|
|
1601
1620
|
case 'Initial':
|
|
1602
|
-
rawMarginRatio =
|
|
1621
|
+
rawMarginRatio = Math.max(
|
|
1622
|
+
market.marginRatioInitial,
|
|
1623
|
+
this.getUserAccount().maxMarginRatio
|
|
1624
|
+
);
|
|
1603
1625
|
break;
|
|
1604
1626
|
case 'Maintenance':
|
|
1605
1627
|
rawMarginRatio = market.marginRatioMaintenance;
|
|
@@ -1623,7 +1645,8 @@ export class User {
|
|
|
1623
1645
|
let marginRatio = calculateMarketMarginRatio(
|
|
1624
1646
|
market,
|
|
1625
1647
|
maxSize,
|
|
1626
|
-
marginCategory
|
|
1648
|
+
marginCategory,
|
|
1649
|
+
this.getUserAccount().maxMarginRatio
|
|
1627
1650
|
);
|
|
1628
1651
|
|
|
1629
1652
|
// use more fesible size since imf factor activated
|
|
@@ -1643,7 +1666,8 @@ export class User {
|
|
|
1643
1666
|
marginRatio = calculateMarketMarginRatio(
|
|
1644
1667
|
market,
|
|
1645
1668
|
targetSize,
|
|
1646
|
-
marginCategory
|
|
1669
|
+
marginCategory,
|
|
1670
|
+
this.getUserAccount().maxMarginRatio
|
|
1647
1671
|
);
|
|
1648
1672
|
attempts += 1;
|
|
1649
1673
|
}
|
|
@@ -2177,6 +2201,7 @@ export class User {
|
|
|
2177
2201
|
: this.getPerpPositionValue(targetMarketIndex, oracleData);
|
|
2178
2202
|
|
|
2179
2203
|
let maxPositionSize = this.getPerpBuyingPower(targetMarketIndex, lpBuffer);
|
|
2204
|
+
|
|
2180
2205
|
if (maxPositionSize.gte(ZERO)) {
|
|
2181
2206
|
if (oppositeSizeValueUSDC.eq(ZERO)) {
|
|
2182
2207
|
// case 1 : Regular trade where current total position less than max, and no opposite position to account for
|
|
@@ -2262,7 +2287,8 @@ export class User {
|
|
|
2262
2287
|
ZERO,
|
|
2263
2288
|
isVariant(direction, 'long')
|
|
2264
2289
|
? SpotBalanceType.DEPOSIT
|
|
2265
|
-
: SpotBalanceType.BORROW
|
|
2290
|
+
: SpotBalanceType.BORROW,
|
|
2291
|
+
this.getUserAccount().maxMarginRatio
|
|
2266
2292
|
);
|
|
2267
2293
|
|
|
2268
2294
|
let tradeAmount = ZERO;
|
|
@@ -2275,7 +2301,8 @@ export class User {
|
|
|
2275
2301
|
oraclePrice,
|
|
2276
2302
|
'Initial',
|
|
2277
2303
|
this.getTokenAmount(targetMarketIndex).abs(),
|
|
2278
|
-
SpotBalanceType.BORROW
|
|
2304
|
+
SpotBalanceType.BORROW,
|
|
2305
|
+
this.getUserAccount().maxMarginRatio
|
|
2279
2306
|
);
|
|
2280
2307
|
freeCollateral = freeCollateral.add(
|
|
2281
2308
|
tradeAmount.mul(new BN(marginRatio)).div(MARGIN_PRECISION)
|
|
@@ -2290,7 +2317,8 @@ export class User {
|
|
|
2290
2317
|
oraclePrice,
|
|
2291
2318
|
'Initial',
|
|
2292
2319
|
this.getTokenAmount(targetMarketIndex),
|
|
2293
|
-
SpotBalanceType.DEPOSIT
|
|
2320
|
+
SpotBalanceType.DEPOSIT,
|
|
2321
|
+
this.getUserAccount().maxMarginRatio
|
|
2294
2322
|
);
|
|
2295
2323
|
freeCollateral = freeCollateral.add(
|
|
2296
2324
|
tradeAmount.mul(new BN(marginRatio)).div(MARGIN_PRECISION)
|
|
@@ -2384,7 +2412,7 @@ export class User {
|
|
|
2384
2412
|
);
|
|
2385
2413
|
|
|
2386
2414
|
const { perpLiabilityValue, perpPnl, spotAssetValue, spotLiabilityValue } =
|
|
2387
|
-
this.getLeverageComponents();
|
|
2415
|
+
this.getLeverageComponents(undefined, 'Initial');
|
|
2388
2416
|
|
|
2389
2417
|
if (!calculateSwap) {
|
|
2390
2418
|
calculateSwap = (inSwap: BN) => {
|
|
@@ -2583,7 +2611,8 @@ export class User {
|
|
|
2583
2611
|
spotPosition,
|
|
2584
2612
|
spotMarketAccount,
|
|
2585
2613
|
strictOraclePrice,
|
|
2586
|
-
marginCategory
|
|
2614
|
+
marginCategory,
|
|
2615
|
+
this.getUserAccount().maxMarginRatio
|
|
2587
2616
|
);
|
|
2588
2617
|
|
|
2589
2618
|
return freeCollateralContribution;
|
|
@@ -2606,7 +2635,8 @@ export class User {
|
|
|
2606
2635
|
spotPosition,
|
|
2607
2636
|
spotMarketAccount,
|
|
2608
2637
|
strictOraclePrice,
|
|
2609
|
-
'Initial'
|
|
2638
|
+
'Initial',
|
|
2639
|
+
this.getUserAccount().maxMarginRatio
|
|
2610
2640
|
);
|
|
2611
2641
|
|
|
2612
2642
|
if (tokenValue.gte(ZERO)) {
|
|
@@ -2678,7 +2708,7 @@ export class User {
|
|
|
2678
2708
|
);
|
|
2679
2709
|
|
|
2680
2710
|
const { perpLiabilityValue, perpPnl, spotAssetValue, spotLiabilityValue } =
|
|
2681
|
-
this.getLeverageComponents();
|
|
2711
|
+
this.getLeverageComponents(undefined, 'Initial');
|
|
2682
2712
|
|
|
2683
2713
|
const inPositionAfter = this.cloneAndUpdateSpotPosition(
|
|
2684
2714
|
inSpotPosition,
|
|
@@ -3225,7 +3255,8 @@ export class User {
|
|
|
3225
3255
|
calculateMarketMarginRatio(
|
|
3226
3256
|
perpMarket,
|
|
3227
3257
|
worstCaseBaseAmount.abs(),
|
|
3228
|
-
marginCategory
|
|
3258
|
+
marginCategory,
|
|
3259
|
+
this.getUserAccount().maxMarginRatio
|
|
3229
3260
|
)
|
|
3230
3261
|
);
|
|
3231
3262
|
|
|
@@ -3349,7 +3380,8 @@ export class User {
|
|
|
3349
3380
|
spotPosition,
|
|
3350
3381
|
spotMarketAccount,
|
|
3351
3382
|
strictOraclePrice,
|
|
3352
|
-
marginCategory
|
|
3383
|
+
marginCategory,
|
|
3384
|
+
this.getUserAccount().maxMarginRatio
|
|
3353
3385
|
);
|
|
3354
3386
|
|
|
3355
3387
|
netQuoteValue = netQuoteValue.add(ordersValue);
|
|
@@ -3393,7 +3425,8 @@ export class User {
|
|
|
3393
3425
|
baseAssetValue,
|
|
3394
3426
|
oraclePriceData.price,
|
|
3395
3427
|
spotMarketAccount,
|
|
3396
|
-
marginCategory
|
|
3428
|
+
marginCategory,
|
|
3429
|
+
this.getUserAccount().maxMarginRatio
|
|
3397
3430
|
);
|
|
3398
3431
|
|
|
3399
3432
|
if (netQuoteValue.lt(ZERO)) {
|
package/tests/amm/test.ts
CHANGED
|
@@ -568,7 +568,8 @@ describe('AMM Tests', () => {
|
|
|
568
568
|
mockMarket1.amm.maxBaseAssetReserve = mockMarket1.amm.baseAssetReserve.add(
|
|
569
569
|
new BN(1234835)
|
|
570
570
|
);
|
|
571
|
-
mockMarket1.amm.minBaseAssetReserve =
|
|
571
|
+
mockMarket1.amm.minBaseAssetReserve =
|
|
572
|
+
mockMarket1.amm.baseAssetReserve.sub(BASE_PRECISION);
|
|
572
573
|
mockMarket1.amm.quoteAssetReserve = new BN(cc).mul(BASE_PRECISION);
|
|
573
574
|
mockMarket1.amm.pegMultiplier = new BN(18.32 * PEG_PRECISION.toNumber());
|
|
574
575
|
mockMarket1.amm.sqrtK = new BN(cc).mul(BASE_PRECISION);
|
|
@@ -790,14 +791,14 @@ describe('AMM Tests', () => {
|
|
|
790
791
|
assert(totalAskSize.sub(openAsks.abs()).lte(new BN(5))); // only tiny rounding errors
|
|
791
792
|
});
|
|
792
793
|
|
|
793
|
-
|
|
794
794
|
it('orderbook L2 gen (4 topOfBookQuoteAmounts, 10 numOrders, low bid liquidity)', async () => {
|
|
795
795
|
const myMockPerpMarkets = _.cloneDeep(mockPerpMarkets);
|
|
796
796
|
|
|
797
797
|
const mockMarket1: PerpMarketAccount = myMockPerpMarkets[0];
|
|
798
798
|
const cc = 38104569;
|
|
799
799
|
mockMarket1.amm.baseAssetReserve = new BN(cc).mul(BASE_PRECISION);
|
|
800
|
-
mockMarket1.amm.maxBaseAssetReserve =
|
|
800
|
+
mockMarket1.amm.maxBaseAssetReserve =
|
|
801
|
+
mockMarket1.amm.baseAssetReserve.add(BASE_PRECISION); // only 1 base
|
|
801
802
|
mockMarket1.amm.minBaseAssetReserve = mockMarket1.amm.baseAssetReserve.div(
|
|
802
803
|
new BN(2)
|
|
803
804
|
);
|
|
@@ -873,14 +874,15 @@ describe('AMM Tests', () => {
|
|
|
873
874
|
assert(totalAskSize.sub(openAsks.abs()).lte(new BN(5))); // only tiny rounding errors
|
|
874
875
|
});
|
|
875
876
|
|
|
876
|
-
|
|
877
877
|
it('orderbook L2 gen (4 topOfBookQuoteAmounts, 10 numOrders, low ask liquidity)', async () => {
|
|
878
878
|
const myMockPerpMarkets = _.cloneDeep(mockPerpMarkets);
|
|
879
879
|
|
|
880
880
|
const mockMarket1: PerpMarketAccount = myMockPerpMarkets[0];
|
|
881
881
|
const cc = 38104569;
|
|
882
882
|
mockMarket1.amm.baseAssetReserve = new BN(cc).mul(BASE_PRECISION);
|
|
883
|
-
mockMarket1.amm.maxBaseAssetReserve = mockMarket1.amm.baseAssetReserve.add(
|
|
883
|
+
mockMarket1.amm.maxBaseAssetReserve = mockMarket1.amm.baseAssetReserve.add(
|
|
884
|
+
BASE_PRECISION.mul(new BN(1000))
|
|
885
|
+
); // 1000 base
|
|
884
886
|
mockMarket1.amm.minBaseAssetReserve = mockMarket1.amm.baseAssetReserve.sub(
|
|
885
887
|
BASE_PRECISION.div(new BN(2))
|
|
886
888
|
); // only .5 base
|
package/tests/auctions/test.ts
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import {PRICE_PRECISION, BN, deriveOracleAuctionParams} from
|
|
2
|
-
import {assert} from
|
|
3
|
-
import {PositionDirection} from
|
|
1
|
+
import { PRICE_PRECISION, BN, deriveOracleAuctionParams } from '../../src';
|
|
2
|
+
import { assert } from 'chai';
|
|
3
|
+
import { PositionDirection } from '../../lib';
|
|
4
4
|
|
|
5
5
|
describe('Auction Tests', () => {
|
|
6
|
-
|
|
7
6
|
it('deriveOracleAuctionParams', async () => {
|
|
8
7
|
let oraclePrice = new BN(100).mul(PRICE_PRECISION);
|
|
9
8
|
let auctionStartPrice = new BN(90).mul(PRICE_PRECISION);
|
|
@@ -18,9 +17,15 @@ describe('Auction Tests', () => {
|
|
|
18
17
|
limitPrice,
|
|
19
18
|
});
|
|
20
19
|
|
|
21
|
-
assert(
|
|
22
|
-
|
|
23
|
-
|
|
20
|
+
assert(
|
|
21
|
+
oracleOrderParams.auctionStartPrice.eq(new BN(-10).mul(PRICE_PRECISION))
|
|
22
|
+
);
|
|
23
|
+
assert(
|
|
24
|
+
oracleOrderParams.auctionEndPrice.eq(new BN(10).mul(PRICE_PRECISION))
|
|
25
|
+
);
|
|
26
|
+
assert(
|
|
27
|
+
oracleOrderParams.oraclePriceOffset === 20 * PRICE_PRECISION.toNumber()
|
|
28
|
+
);
|
|
24
29
|
|
|
25
30
|
oracleOrderParams = deriveOracleAuctionParams({
|
|
26
31
|
direction: PositionDirection.LONG,
|
|
@@ -47,9 +52,15 @@ describe('Auction Tests', () => {
|
|
|
47
52
|
limitPrice,
|
|
48
53
|
});
|
|
49
54
|
|
|
50
|
-
assert(
|
|
51
|
-
|
|
52
|
-
|
|
55
|
+
assert(
|
|
56
|
+
oracleOrderParams.auctionStartPrice.eq(new BN(10).mul(PRICE_PRECISION))
|
|
57
|
+
);
|
|
58
|
+
assert(
|
|
59
|
+
oracleOrderParams.auctionEndPrice.eq(new BN(-10).mul(PRICE_PRECISION))
|
|
60
|
+
);
|
|
61
|
+
assert(
|
|
62
|
+
oracleOrderParams.oraclePriceOffset === -20 * PRICE_PRECISION.toNumber()
|
|
63
|
+
);
|
|
53
64
|
|
|
54
65
|
oracleOrderParams = deriveOracleAuctionParams({
|
|
55
66
|
direction: PositionDirection.SHORT,
|
|
@@ -63,4 +74,4 @@ describe('Auction Tests', () => {
|
|
|
63
74
|
assert(oracleOrderParams.auctionEndPrice.eq(new BN(0)));
|
|
64
75
|
assert(oracleOrderParams.oraclePriceOffset === -1);
|
|
65
76
|
});
|
|
66
|
-
});
|
|
77
|
+
});
|
package/tests/dlob/helpers.ts
CHANGED
|
@@ -21,7 +21,9 @@ import {
|
|
|
21
21
|
OrderRecord,
|
|
22
22
|
ZERO,
|
|
23
23
|
ContractTier,
|
|
24
|
-
SPOT_MARKET_CUMULATIVE_INTEREST_PRECISION
|
|
24
|
+
SPOT_MARKET_CUMULATIVE_INTEREST_PRECISION,
|
|
25
|
+
SPOT_MARKET_WEIGHT_PRECISION,
|
|
26
|
+
PRICE_PRECISION,
|
|
25
27
|
} from '../../src';
|
|
26
28
|
|
|
27
29
|
export const mockPerpPosition: PerpPosition = {
|
|
@@ -263,7 +265,7 @@ export const mockSpotMarkets: Array<SpotMarketAccount> = [
|
|
|
263
265
|
status: MarketStatus.ACTIVE,
|
|
264
266
|
assetTier: AssetTier.COLLATERAL,
|
|
265
267
|
name: [],
|
|
266
|
-
maxTokenDeposits: new BN(1000000),
|
|
268
|
+
maxTokenDeposits: new BN(1000000 * QUOTE_PRECISION.toNumber()),
|
|
267
269
|
marketIndex: 0,
|
|
268
270
|
pubkey: PublicKey.default,
|
|
269
271
|
mint: DevnetSpotMarkets[0].mint,
|
|
@@ -300,10 +302,10 @@ export const mockSpotMarkets: Array<SpotMarketAccount> = [
|
|
|
300
302
|
lastInterestTs: new BN(0),
|
|
301
303
|
lastTwapTs: new BN(0),
|
|
302
304
|
oracle: PublicKey.default,
|
|
303
|
-
initialAssetWeight:
|
|
304
|
-
maintenanceAssetWeight:
|
|
305
|
-
initialLiabilityWeight:
|
|
306
|
-
maintenanceLiabilityWeight:
|
|
305
|
+
initialAssetWeight: SPOT_MARKET_WEIGHT_PRECISION.toNumber(),
|
|
306
|
+
maintenanceAssetWeight: SPOT_MARKET_WEIGHT_PRECISION.toNumber(),
|
|
307
|
+
initialLiabilityWeight: SPOT_MARKET_WEIGHT_PRECISION.toNumber(),
|
|
308
|
+
maintenanceLiabilityWeight: SPOT_MARKET_WEIGHT_PRECISION.toNumber(),
|
|
307
309
|
scaleInitialAssetWeightStart: new BN(0),
|
|
308
310
|
imfFactor: 0,
|
|
309
311
|
withdrawGuardThreshold: new BN(0),
|
|
@@ -325,18 +327,18 @@ export const mockSpotMarkets: Array<SpotMarketAccount> = [
|
|
|
325
327
|
flashLoanInitialTokenAmount: new BN(0),
|
|
326
328
|
oracleSource: OracleSource.PYTH,
|
|
327
329
|
historicalOracleData: {
|
|
328
|
-
lastOraclePrice:
|
|
330
|
+
lastOraclePrice: PRICE_PRECISION,
|
|
329
331
|
lastOracleConf: new BN(0),
|
|
330
332
|
lastOracleDelay: new BN(0),
|
|
331
|
-
lastOraclePriceTwap:
|
|
332
|
-
lastOraclePriceTwap5Min:
|
|
333
|
+
lastOraclePriceTwap: PRICE_PRECISION,
|
|
334
|
+
lastOraclePriceTwap5Min: PRICE_PRECISION,
|
|
333
335
|
lastOraclePriceTwapTs: new BN(0),
|
|
334
336
|
},
|
|
335
337
|
historicalIndexData: {
|
|
336
|
-
lastIndexBidPrice:
|
|
337
|
-
lastIndexAskPrice:
|
|
338
|
-
lastIndexPriceTwap:
|
|
339
|
-
lastIndexPriceTwap5Min:
|
|
338
|
+
lastIndexBidPrice: PRICE_PRECISION,
|
|
339
|
+
lastIndexAskPrice: PRICE_PRECISION,
|
|
340
|
+
lastIndexPriceTwap: PRICE_PRECISION,
|
|
341
|
+
lastIndexPriceTwap5Min: PRICE_PRECISION,
|
|
340
342
|
lastIndexPriceTwapTs: new BN(0),
|
|
341
343
|
},
|
|
342
344
|
},
|
package/tests/user/test.ts
CHANGED
|
@@ -16,11 +16,12 @@ import {
|
|
|
16
16
|
StrictOraclePrice,
|
|
17
17
|
LAMPORTS_PRECISION,
|
|
18
18
|
SPOT_MARKET_CUMULATIVE_INTEREST_PRECISION,
|
|
19
|
-
SpotBalanceType
|
|
19
|
+
SpotBalanceType,
|
|
20
|
+
MARGIN_PRECISION,
|
|
20
21
|
} from '../../src';
|
|
21
22
|
import { MockUserMap, mockPerpMarkets, mockSpotMarkets } from '../dlob/helpers';
|
|
22
23
|
import { assert } from '../../src/assert/assert';
|
|
23
|
-
import {mockUserAccount} from './helpers';
|
|
24
|
+
import { mockUserAccount } from './helpers';
|
|
24
25
|
import * as _ from 'lodash';
|
|
25
26
|
|
|
26
27
|
async function makeMockUser(
|
|
@@ -260,15 +261,16 @@ describe('User Tests', () => {
|
|
|
260
261
|
'spot cumulativeDepositInterest:',
|
|
261
262
|
mockSpotMarkets[0].cumulativeDepositInterest.toString()
|
|
262
263
|
);
|
|
263
|
-
|
|
264
|
-
assert(mockUser.
|
|
264
|
+
const expectedAmount = new BN('10000000000');
|
|
265
|
+
assert(mockUser.getTokenAmount(0).eq(expectedAmount));
|
|
266
|
+
assert(mockUser.getNetSpotMarketValue().eq(expectedAmount));
|
|
265
267
|
assert(
|
|
266
268
|
mockUser
|
|
267
269
|
.getSpotMarketAssetAndLiabilityValue()
|
|
268
270
|
.totalLiabilityValue.eq(ZERO)
|
|
269
271
|
);
|
|
270
272
|
|
|
271
|
-
assert(mockUser.getFreeCollateral().
|
|
273
|
+
assert(mockUser.getFreeCollateral().gt(ZERO));
|
|
272
274
|
const upnl = mockUser.getUnrealizedPNL(true, 0, undefined, false);
|
|
273
275
|
console.log('upnl:', upnl.toString());
|
|
274
276
|
assert(upnl.eq(new BN('0'))); // $10
|
|
@@ -277,7 +279,7 @@ describe('User Tests', () => {
|
|
|
277
279
|
console.log(liqResult);
|
|
278
280
|
assert(liqResult.canBeLiquidated == false);
|
|
279
281
|
assert(liqResult.marginRequirement.eq(new BN('0'))); //10x maint leverage
|
|
280
|
-
assert(liqResult.totalCollateral.eq(
|
|
282
|
+
assert(liqResult.totalCollateral.eq(expectedAmount));
|
|
281
283
|
|
|
282
284
|
console.log(mockUser.getHealth());
|
|
283
285
|
assert(mockUser.getHealth() == 100);
|
|
@@ -286,7 +288,9 @@ describe('User Tests', () => {
|
|
|
286
288
|
'getMaxLeverageForPerp:',
|
|
287
289
|
mockUser.getMaxLeverageForPerp(0).toString()
|
|
288
290
|
);
|
|
289
|
-
assert(mockUser.getMaxLeverageForPerp(0).eq(new BN('
|
|
291
|
+
assert(mockUser.getMaxLeverageForPerp(0).eq(new BN('50000'))); // 5x
|
|
292
|
+
assert(mockUser.getMaxLeverageForPerp(0, 'Maintenance').eq(new BN('100000'))); // 10x
|
|
293
|
+
|
|
290
294
|
});
|
|
291
295
|
|
|
292
296
|
it('worst case token amount', async () => {
|
|
@@ -303,57 +307,200 @@ describe('User Tests', () => {
|
|
|
303
307
|
|
|
304
308
|
let spotPosition = Object.assign({}, myMockUserAccount.spotPositions[1], {
|
|
305
309
|
marketIndex: 1,
|
|
306
|
-
openBids: new BN(100).mul(
|
|
310
|
+
openBids: new BN(100).mul(LAMPORTS_PRECISION),
|
|
307
311
|
});
|
|
308
312
|
|
|
309
|
-
let worstCase = getWorstCaseTokenAmounts(
|
|
313
|
+
let worstCase = getWorstCaseTokenAmounts(
|
|
314
|
+
spotPosition,
|
|
315
|
+
solMarket,
|
|
316
|
+
strictOraclePrice,
|
|
317
|
+
'Initial'
|
|
318
|
+
);
|
|
310
319
|
|
|
311
|
-
assert(worstCase.tokenAmount.eq(new BN(100).mul(
|
|
320
|
+
assert(worstCase.tokenAmount.eq(new BN(100).mul(LAMPORTS_PRECISION))); // 100
|
|
312
321
|
assert(worstCase.tokenValue.eq(new BN(10000).mul(PRICE_PRECISION))); // $10k
|
|
313
322
|
assert(worstCase.weightedTokenValue.eq(new BN(8000).mul(PRICE_PRECISION))); // $8k
|
|
314
323
|
assert(worstCase.ordersValue.eq(new BN(-10000).mul(PRICE_PRECISION))); // -$10k
|
|
315
|
-
assert(
|
|
324
|
+
assert(
|
|
325
|
+
worstCase.freeCollateralContribution.eq(
|
|
326
|
+
new BN(-2000).mul(QUOTE_PRECISION)
|
|
327
|
+
)
|
|
328
|
+
); // -$2k
|
|
316
329
|
|
|
317
330
|
spotPosition = Object.assign({}, myMockUserAccount.spotPositions[1], {
|
|
318
331
|
marketIndex: 1,
|
|
319
332
|
scaledBalance: new BN(100).mul(SPOT_MARKET_BALANCE_PRECISION),
|
|
320
|
-
openBids: new BN(100).mul(
|
|
333
|
+
openBids: new BN(100).mul(LAMPORTS_PRECISION),
|
|
321
334
|
});
|
|
322
335
|
|
|
323
|
-
worstCase = getWorstCaseTokenAmounts(
|
|
336
|
+
worstCase = getWorstCaseTokenAmounts(
|
|
337
|
+
spotPosition,
|
|
338
|
+
solMarket,
|
|
339
|
+
strictOraclePrice,
|
|
340
|
+
'Initial'
|
|
341
|
+
);
|
|
324
342
|
|
|
325
343
|
assert(worstCase.tokenAmount.eq(new BN(200).mul(LAMPORTS_PRECISION))); // 200
|
|
326
344
|
assert(worstCase.tokenValue.eq(new BN(20000).mul(PRICE_PRECISION))); // $20k
|
|
327
345
|
assert(worstCase.weightedTokenValue.eq(new BN(16000).mul(PRICE_PRECISION))); // $16k
|
|
328
346
|
assert(worstCase.ordersValue.eq(new BN(-10000).mul(PRICE_PRECISION))); // -$10k
|
|
329
|
-
assert(
|
|
347
|
+
assert(
|
|
348
|
+
worstCase.freeCollateralContribution.eq(new BN(6000).mul(QUOTE_PRECISION))
|
|
349
|
+
); // $6k
|
|
330
350
|
|
|
331
351
|
spotPosition = Object.assign({}, myMockUserAccount.spotPositions[1], {
|
|
332
352
|
marketIndex: 1,
|
|
333
|
-
openAsks: new BN(-100).mul(
|
|
353
|
+
openAsks: new BN(-100).mul(LAMPORTS_PRECISION),
|
|
334
354
|
});
|
|
335
355
|
|
|
336
|
-
worstCase = getWorstCaseTokenAmounts(
|
|
356
|
+
worstCase = getWorstCaseTokenAmounts(
|
|
357
|
+
spotPosition,
|
|
358
|
+
solMarket,
|
|
359
|
+
strictOraclePrice,
|
|
360
|
+
'Initial'
|
|
361
|
+
);
|
|
337
362
|
|
|
338
|
-
assert(worstCase.tokenAmount.eq(new BN(-100).mul(
|
|
363
|
+
assert(worstCase.tokenAmount.eq(new BN(-100).mul(LAMPORTS_PRECISION)));
|
|
339
364
|
assert(worstCase.tokenValue.eq(new BN(-10000).mul(PRICE_PRECISION))); // -$10k
|
|
340
|
-
assert(
|
|
365
|
+
assert(
|
|
366
|
+
worstCase.weightedTokenValue.eq(new BN(-12000).mul(PRICE_PRECISION))
|
|
367
|
+
); // -$12k
|
|
341
368
|
assert(worstCase.ordersValue.eq(new BN(10000).mul(PRICE_PRECISION))); // $10k
|
|
342
|
-
assert(
|
|
369
|
+
assert(
|
|
370
|
+
worstCase.freeCollateralContribution.eq(
|
|
371
|
+
new BN(-2000).mul(QUOTE_PRECISION)
|
|
372
|
+
)
|
|
373
|
+
); // -$2k
|
|
343
374
|
|
|
344
375
|
spotPosition = Object.assign({}, myMockUserAccount.spotPositions[1], {
|
|
345
376
|
marketIndex: 1,
|
|
346
377
|
balanceType: SpotBalanceType.BORROW,
|
|
347
378
|
scaledBalance: new BN(100).mul(SPOT_MARKET_BALANCE_PRECISION),
|
|
348
|
-
openAsks: new BN(-100).mul(
|
|
379
|
+
openAsks: new BN(-100).mul(LAMPORTS_PRECISION),
|
|
349
380
|
});
|
|
350
381
|
|
|
351
|
-
worstCase = getWorstCaseTokenAmounts(
|
|
382
|
+
worstCase = getWorstCaseTokenAmounts(
|
|
383
|
+
spotPosition,
|
|
384
|
+
solMarket,
|
|
385
|
+
strictOraclePrice,
|
|
386
|
+
'Initial'
|
|
387
|
+
);
|
|
352
388
|
|
|
353
|
-
assert(worstCase.tokenAmount.eq(new BN(-200).mul(
|
|
389
|
+
assert(worstCase.tokenAmount.eq(new BN(-200).mul(LAMPORTS_PRECISION)));
|
|
354
390
|
assert(worstCase.tokenValue.eq(new BN(-20000).mul(PRICE_PRECISION))); // -$20k
|
|
355
|
-
assert(
|
|
391
|
+
assert(
|
|
392
|
+
worstCase.weightedTokenValue.eq(new BN(-24000).mul(PRICE_PRECISION))
|
|
393
|
+
); // -$24k
|
|
356
394
|
assert(worstCase.ordersValue.eq(new BN(10000).mul(PRICE_PRECISION))); // $10k
|
|
357
|
-
assert(
|
|
395
|
+
assert(
|
|
396
|
+
worstCase.freeCollateralContribution.eq(
|
|
397
|
+
new BN(-14000).mul(QUOTE_PRECISION)
|
|
398
|
+
)
|
|
399
|
+
); // -$2k
|
|
400
|
+
});
|
|
401
|
+
|
|
402
|
+
|
|
403
|
+
it('custom margin ratio (sol spot)', async() => {
|
|
404
|
+
const myMockUserAccount = _.cloneDeep(mockUserAccount);
|
|
405
|
+
|
|
406
|
+
const solMarket = Object.assign({}, _.cloneDeep(mockSpotMarkets[1]), {
|
|
407
|
+
initialAssetWeight: 8000,
|
|
408
|
+
initialLiabilityWeight: 12000,
|
|
409
|
+
cumulativeDepositInterest: SPOT_MARKET_CUMULATIVE_INTEREST_PRECISION,
|
|
410
|
+
cumulativeBorrowInterest: SPOT_MARKET_CUMULATIVE_INTEREST_PRECISION,
|
|
411
|
+
});
|
|
412
|
+
|
|
413
|
+
// $25
|
|
414
|
+
const strictOraclePrice = new StrictOraclePrice(PRICE_PRECISION.muln(25));
|
|
415
|
+
|
|
416
|
+
const spotPosition = Object.assign({}, myMockUserAccount.spotPositions[1], {
|
|
417
|
+
marketIndex: 1,
|
|
418
|
+
openBids: new BN(100).mul(LAMPORTS_PRECISION),
|
|
419
|
+
});
|
|
420
|
+
|
|
421
|
+
const worstCase = getWorstCaseTokenAmounts(
|
|
422
|
+
spotPosition,
|
|
423
|
+
solMarket,
|
|
424
|
+
strictOraclePrice,
|
|
425
|
+
'Initial',
|
|
426
|
+
myMockUserAccount.maxMarginRatio
|
|
427
|
+
);
|
|
428
|
+
|
|
429
|
+
console.log(worstCase);
|
|
430
|
+
assert(worstCase.weight.eq(new BN(8000)));
|
|
431
|
+
|
|
432
|
+
|
|
433
|
+
myMockUserAccount.maxMarginRatio = MARGIN_PRECISION.toNumber(); // max 1x pls
|
|
434
|
+
|
|
435
|
+
|
|
436
|
+
const worstCaseAfter = getWorstCaseTokenAmounts(
|
|
437
|
+
spotPosition,
|
|
438
|
+
solMarket,
|
|
439
|
+
strictOraclePrice,
|
|
440
|
+
'Initial',
|
|
441
|
+
myMockUserAccount.maxMarginRatio
|
|
442
|
+
);
|
|
443
|
+
|
|
444
|
+
console.log(worstCaseAfter);
|
|
445
|
+
assert(worstCaseAfter.weight.eq(new BN(0))); // not allowed to increase exposure
|
|
446
|
+
|
|
447
|
+
});
|
|
448
|
+
|
|
449
|
+
it('custom margin ratio (sol perp)', async() => {
|
|
450
|
+
const myMockPerpMarkets = _.cloneDeep(mockPerpMarkets);
|
|
451
|
+
const myMockSpotMarkets = _.cloneDeep(mockSpotMarkets);
|
|
452
|
+
const myMockUserAccount = _.cloneDeep(mockUserAccount);
|
|
453
|
+
// myMockPerpMarkets[0].imfFactor = 550;
|
|
454
|
+
myMockPerpMarkets[0].marginRatioInitial = 2000; // 5x
|
|
455
|
+
myMockPerpMarkets[0].marginRatioMaintenance = 1000; // 10x
|
|
456
|
+
|
|
457
|
+
myMockSpotMarkets[0].initialAssetWeight = 1000;
|
|
458
|
+
myMockSpotMarkets[0].initialLiabilityWeight = 1000;
|
|
459
|
+
|
|
460
|
+
myMockUserAccount.spotPositions[0].scaledBalance = new BN(
|
|
461
|
+
10000 * SPOT_MARKET_BALANCE_PRECISION.toNumber()
|
|
462
|
+
); //10k
|
|
463
|
+
|
|
464
|
+
const mockUser: User = await makeMockUser(
|
|
465
|
+
myMockPerpMarkets,
|
|
466
|
+
myMockSpotMarkets,
|
|
467
|
+
myMockUserAccount,
|
|
468
|
+
[1, 1, 1, 1, 1, 1, 1, 1],
|
|
469
|
+
[1, 1, 1, 1, 1, 1, 1, 1]
|
|
470
|
+
);
|
|
471
|
+
|
|
472
|
+
|
|
473
|
+
assert(mockUser.getTokenAmount(0).eq(new BN('10000000000')));
|
|
474
|
+
assert(mockUser.getNetSpotMarketValue().eq(new BN('10000000000')));
|
|
475
|
+
assert(
|
|
476
|
+
mockUser
|
|
477
|
+
.getSpotMarketAssetAndLiabilityValue()
|
|
478
|
+
.totalLiabilityValue.eq(ZERO)
|
|
479
|
+
);
|
|
480
|
+
|
|
481
|
+
assert(mockUser.getFreeCollateral().gt(ZERO));
|
|
482
|
+
|
|
483
|
+
let iLev = mockUser.getMaxLeverageForPerp(0, 'Initial').toNumber();
|
|
484
|
+
let mLev = mockUser.getMaxLeverageForPerp(0, 'Maintenance').toNumber();
|
|
485
|
+
console.log(iLev, mLev);
|
|
486
|
+
assert(iLev == 5000);
|
|
487
|
+
assert(mLev == 10000);
|
|
488
|
+
|
|
489
|
+
myMockUserAccount.maxMarginRatio = MARGIN_PRECISION.div(new BN(2)).toNumber(); // 2x max pls
|
|
490
|
+
|
|
491
|
+
const mockUser2: User = await makeMockUser(
|
|
492
|
+
myMockPerpMarkets,
|
|
493
|
+
myMockSpotMarkets,
|
|
494
|
+
myMockUserAccount,
|
|
495
|
+
[1, 1, 1, 1, 1, 1, 1, 1],
|
|
496
|
+
[1, 1, 1, 1, 1, 1, 1, 1]
|
|
497
|
+
);
|
|
498
|
+
iLev = mockUser2.getMaxLeverageForPerp(0, 'Initial').toNumber();
|
|
499
|
+
mLev = mockUser2.getMaxLeverageForPerp(0, 'Maintenance').toNumber();
|
|
500
|
+
console.log(iLev, mLev);
|
|
501
|
+
|
|
502
|
+
assert(iLev == 2000);
|
|
503
|
+
assert(mLev == 10000);
|
|
504
|
+
|
|
358
505
|
});
|
|
359
506
|
});
|