@drift-labs/sdk 2.92.0-beta.2 → 2.93.0-beta.0
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/bun.lockb +0 -0
- package/lib/adminClient.d.ts +2 -0
- package/lib/adminClient.js +17 -0
- package/lib/constants/numericConstants.d.ts +1 -0
- package/lib/constants/numericConstants.js +2 -1
- package/lib/dlob/orderBookLevels.js +1 -1
- package/lib/driftClient.js +1 -0
- package/lib/idl/drift.json +29 -3
- package/lib/math/amm.d.ts +5 -4
- package/lib/math/amm.js +35 -8
- package/lib/math/margin.d.ts +14 -1
- package/lib/math/margin.js +45 -7
- package/lib/types.d.ts +3 -0
- package/lib/types.js +1 -0
- package/lib/user.d.ts +15 -6
- package/lib/user.js +118 -75
- package/package.json +1 -1
- package/src/adminClient.ts +30 -0
- package/src/constants/numericConstants.ts +2 -0
- package/src/dlob/orderBookLevels.ts +2 -1
- package/src/driftClient.ts +1 -0
- package/src/idl/drift.json +29 -3
- package/src/math/amm.ts +55 -7
- package/src/math/margin.ts +70 -5
- package/src/types.ts +1 -0
- package/src/user.ts +201 -92
package/src/math/margin.ts
CHANGED
|
@@ -5,6 +5,8 @@ import {
|
|
|
5
5
|
ZERO,
|
|
6
6
|
BID_ASK_SPREAD_PRECISION,
|
|
7
7
|
AMM_RESERVE_PRECISION,
|
|
8
|
+
MAX_PREDICTION_PRICE,
|
|
9
|
+
BASE_PRECISION,
|
|
8
10
|
} from '../constants/numericConstants';
|
|
9
11
|
import { BN } from '@coral-xyz/anchor';
|
|
10
12
|
import { OraclePriceData } from '../oracles/types';
|
|
@@ -99,6 +101,14 @@ export function calculateOraclePriceForPerpMargin(
|
|
|
99
101
|
return marginPrice;
|
|
100
102
|
}
|
|
101
103
|
|
|
104
|
+
/**
|
|
105
|
+
* This is _not_ the same as liability value as for prediction markets, the liability for the short in prediction market is (1 - oracle price) * base
|
|
106
|
+
* See {@link calculatePerpLiabilityValue} to get the liabiltiy value
|
|
107
|
+
* @param market
|
|
108
|
+
* @param perpPosition
|
|
109
|
+
* @param oraclePriceData
|
|
110
|
+
* @param includeOpenOrders
|
|
111
|
+
*/
|
|
102
112
|
export function calculateBaseAssetValueWithOracle(
|
|
103
113
|
market: PerpMarketAccount,
|
|
104
114
|
perpPosition: PerpPosition,
|
|
@@ -111,21 +121,76 @@ export function calculateBaseAssetValueWithOracle(
|
|
|
111
121
|
}
|
|
112
122
|
|
|
113
123
|
const baseAssetAmount = includeOpenOrders
|
|
114
|
-
? calculateWorstCaseBaseAssetAmount(
|
|
124
|
+
? calculateWorstCaseBaseAssetAmount(
|
|
125
|
+
perpPosition,
|
|
126
|
+
market,
|
|
127
|
+
oraclePriceData.price
|
|
128
|
+
)
|
|
115
129
|
: perpPosition.baseAssetAmount;
|
|
116
130
|
|
|
117
131
|
return baseAssetAmount.abs().mul(price).div(AMM_RESERVE_PRECISION);
|
|
118
132
|
}
|
|
119
133
|
|
|
120
134
|
export function calculateWorstCaseBaseAssetAmount(
|
|
121
|
-
perpPosition: PerpPosition
|
|
135
|
+
perpPosition: PerpPosition,
|
|
136
|
+
perpMarket: PerpMarketAccount,
|
|
137
|
+
oraclePrice: BN
|
|
122
138
|
): BN {
|
|
139
|
+
return calculateWorstCasePerpLiabilityValue(
|
|
140
|
+
perpPosition,
|
|
141
|
+
perpMarket,
|
|
142
|
+
oraclePrice
|
|
143
|
+
).worstCaseBaseAssetAmount;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export function calculateWorstCasePerpLiabilityValue(
|
|
147
|
+
perpPosition: PerpPosition,
|
|
148
|
+
perpMarket: PerpMarketAccount,
|
|
149
|
+
oraclePrice: BN
|
|
150
|
+
): { worstCaseBaseAssetAmount: BN; worstCaseLiabilityValue: BN } {
|
|
123
151
|
const allBids = perpPosition.baseAssetAmount.add(perpPosition.openBids);
|
|
124
152
|
const allAsks = perpPosition.baseAssetAmount.add(perpPosition.openAsks);
|
|
125
153
|
|
|
126
|
-
|
|
127
|
-
|
|
154
|
+
const isPredictionMarket = isVariant(perpMarket.contractType, 'prediction');
|
|
155
|
+
const allBidsLiabilityValue = calculatePerpLiabilityValue(
|
|
156
|
+
allBids,
|
|
157
|
+
oraclePrice,
|
|
158
|
+
isPredictionMarket
|
|
159
|
+
);
|
|
160
|
+
const allAsksLiabilityValue = calculatePerpLiabilityValue(
|
|
161
|
+
allAsks,
|
|
162
|
+
oraclePrice,
|
|
163
|
+
isPredictionMarket
|
|
164
|
+
);
|
|
165
|
+
|
|
166
|
+
if (allAsksLiabilityValue.gte(allBidsLiabilityValue)) {
|
|
167
|
+
return {
|
|
168
|
+
worstCaseBaseAssetAmount: allAsks,
|
|
169
|
+
worstCaseLiabilityValue: allAsksLiabilityValue,
|
|
170
|
+
};
|
|
171
|
+
} else {
|
|
172
|
+
return {
|
|
173
|
+
worstCaseBaseAssetAmount: allBids,
|
|
174
|
+
worstCaseLiabilityValue: allBidsLiabilityValue,
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export function calculatePerpLiabilityValue(
|
|
180
|
+
baseAssetAmount: BN,
|
|
181
|
+
oraclePrice: BN,
|
|
182
|
+
isPredictionMarket: boolean
|
|
183
|
+
): BN {
|
|
184
|
+
if (isPredictionMarket) {
|
|
185
|
+
if (baseAssetAmount.gt(ZERO)) {
|
|
186
|
+
return baseAssetAmount.mul(oraclePrice).div(BASE_PRECISION);
|
|
187
|
+
} else {
|
|
188
|
+
return baseAssetAmount
|
|
189
|
+
.abs()
|
|
190
|
+
.mul(MAX_PREDICTION_PRICE.sub(oraclePrice))
|
|
191
|
+
.div(BASE_PRECISION);
|
|
192
|
+
}
|
|
128
193
|
} else {
|
|
129
|
-
return
|
|
194
|
+
return baseAssetAmount.abs().mul(oraclePrice).div(BASE_PRECISION);
|
|
130
195
|
}
|
|
131
196
|
}
|
package/src/types.ts
CHANGED
package/src/user.ts
CHANGED
|
@@ -48,11 +48,13 @@ import {
|
|
|
48
48
|
BN,
|
|
49
49
|
calculateBaseAssetValue,
|
|
50
50
|
calculateMarketMarginRatio,
|
|
51
|
+
calculatePerpLiabilityValue,
|
|
51
52
|
calculatePositionFundingPNL,
|
|
52
53
|
calculatePositionPNL,
|
|
53
54
|
calculateReservePrice,
|
|
54
55
|
calculateSpotMarketMarginRatio,
|
|
55
56
|
calculateUnrealizedAssetWeight,
|
|
57
|
+
calculateWorstCasePerpLiabilityValue,
|
|
56
58
|
divCeil,
|
|
57
59
|
getBalance,
|
|
58
60
|
getSignedTokenAmount,
|
|
@@ -642,8 +644,15 @@ export class User {
|
|
|
642
644
|
undefined,
|
|
643
645
|
true
|
|
644
646
|
)[0];
|
|
647
|
+
|
|
648
|
+
const perpMarket = this.driftClient.getPerpMarketAccount(marketIndex);
|
|
649
|
+
const oraclePriceData = this.getOracleDataForPerpMarket(marketIndex);
|
|
645
650
|
const worstCaseBaseAssetAmount = perpPosition
|
|
646
|
-
? calculateWorstCaseBaseAssetAmount(
|
|
651
|
+
? calculateWorstCaseBaseAssetAmount(
|
|
652
|
+
perpPosition,
|
|
653
|
+
perpMarket,
|
|
654
|
+
oraclePriceData.price
|
|
655
|
+
)
|
|
647
656
|
: ZERO;
|
|
648
657
|
|
|
649
658
|
const freeCollateral = this.getFreeCollateral().sub(collateralBuffer);
|
|
@@ -693,7 +702,7 @@ export class User {
|
|
|
693
702
|
strict = false,
|
|
694
703
|
includeOpenOrders = true
|
|
695
704
|
): BN {
|
|
696
|
-
return this.
|
|
705
|
+
return this.getTotalPerpPositionLiability(
|
|
697
706
|
marginCategory,
|
|
698
707
|
liquidationBuffer,
|
|
699
708
|
includeOpenOrders,
|
|
@@ -1441,7 +1450,7 @@ export class User {
|
|
|
1441
1450
|
return health;
|
|
1442
1451
|
}
|
|
1443
1452
|
|
|
1444
|
-
|
|
1453
|
+
calculateWeightedPerpPositionLiability(
|
|
1445
1454
|
perpPosition: PerpPosition,
|
|
1446
1455
|
marginCategory?: MarginCategory,
|
|
1447
1456
|
liquidationBuffer?: BN,
|
|
@@ -1469,14 +1478,25 @@ export class User {
|
|
|
1469
1478
|
valuationPrice = market.expiryPrice;
|
|
1470
1479
|
}
|
|
1471
1480
|
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1481
|
+
let baseAssetAmount: BN;
|
|
1482
|
+
let liabilityValue;
|
|
1483
|
+
if (includeOpenOrders) {
|
|
1484
|
+
const { worstCaseBaseAssetAmount, worstCaseLiabilityValue } =
|
|
1485
|
+
calculateWorstCasePerpLiabilityValue(
|
|
1486
|
+
perpPosition,
|
|
1487
|
+
market,
|
|
1488
|
+
valuationPrice
|
|
1489
|
+
);
|
|
1490
|
+
baseAssetAmount = worstCaseBaseAssetAmount;
|
|
1491
|
+
liabilityValue = worstCaseLiabilityValue;
|
|
1492
|
+
} else {
|
|
1493
|
+
baseAssetAmount = perpPosition.baseAssetAmount;
|
|
1494
|
+
liabilityValue = calculatePerpLiabilityValue(
|
|
1495
|
+
baseAssetAmount,
|
|
1496
|
+
valuationPrice,
|
|
1497
|
+
isVariant(market.contractType, 'prediction')
|
|
1498
|
+
);
|
|
1499
|
+
}
|
|
1480
1500
|
|
|
1481
1501
|
if (marginCategory) {
|
|
1482
1502
|
let marginRatio = new BN(
|
|
@@ -1513,19 +1533,19 @@ export class User {
|
|
|
1513
1533
|
quotePrice = quoteOraclePriceData.price;
|
|
1514
1534
|
}
|
|
1515
1535
|
|
|
1516
|
-
|
|
1536
|
+
liabilityValue = liabilityValue
|
|
1517
1537
|
.mul(quotePrice)
|
|
1518
1538
|
.div(PRICE_PRECISION)
|
|
1519
1539
|
.mul(marginRatio)
|
|
1520
1540
|
.div(MARGIN_PRECISION);
|
|
1521
1541
|
|
|
1522
1542
|
if (includeOpenOrders) {
|
|
1523
|
-
|
|
1543
|
+
liabilityValue = liabilityValue.add(
|
|
1524
1544
|
new BN(perpPosition.openOrders).mul(OPEN_ORDER_MARGIN_REQUIREMENT)
|
|
1525
1545
|
);
|
|
1526
1546
|
|
|
1527
1547
|
if (perpPosition.lpShares.gt(ZERO)) {
|
|
1528
|
-
|
|
1548
|
+
liabilityValue = liabilityValue.add(
|
|
1529
1549
|
BN.max(
|
|
1530
1550
|
QUOTE_PRECISION,
|
|
1531
1551
|
valuationPrice
|
|
@@ -1539,7 +1559,7 @@ export class User {
|
|
|
1539
1559
|
}
|
|
1540
1560
|
}
|
|
1541
1561
|
|
|
1542
|
-
return
|
|
1562
|
+
return liabilityValue;
|
|
1543
1563
|
}
|
|
1544
1564
|
|
|
1545
1565
|
/**
|
|
@@ -1554,7 +1574,7 @@ export class User {
|
|
|
1554
1574
|
strict = false
|
|
1555
1575
|
): BN {
|
|
1556
1576
|
const perpPosition = this.getPerpPosition(marketIndex);
|
|
1557
|
-
return this.
|
|
1577
|
+
return this.calculateWeightedPerpPositionLiability(
|
|
1558
1578
|
perpPosition,
|
|
1559
1579
|
marginCategory,
|
|
1560
1580
|
liquidationBuffer,
|
|
@@ -1567,7 +1587,7 @@ export class User {
|
|
|
1567
1587
|
* calculates sum of position value across all positions in margin system
|
|
1568
1588
|
* @returns : Precision QUOTE_PRECISION
|
|
1569
1589
|
*/
|
|
1570
|
-
|
|
1590
|
+
getTotalPerpPositionLiability(
|
|
1571
1591
|
marginCategory?: MarginCategory,
|
|
1572
1592
|
liquidationBuffer?: BN,
|
|
1573
1593
|
includeOpenOrders?: boolean,
|
|
@@ -1575,7 +1595,7 @@ export class User {
|
|
|
1575
1595
|
): BN {
|
|
1576
1596
|
return this.getActivePerpPositions().reduce(
|
|
1577
1597
|
(totalPerpValue, perpPosition) => {
|
|
1578
|
-
const baseAssetValue = this.
|
|
1598
|
+
const baseAssetValue = this.calculateWeightedPerpPositionLiability(
|
|
1579
1599
|
perpPosition,
|
|
1580
1600
|
marginCategory,
|
|
1581
1601
|
liquidationBuffer,
|
|
@@ -1589,7 +1609,7 @@ export class User {
|
|
|
1589
1609
|
}
|
|
1590
1610
|
|
|
1591
1611
|
/**
|
|
1592
|
-
* calculates position value
|
|
1612
|
+
* calculates position value based on oracle
|
|
1593
1613
|
* @returns : Precision QUOTE_PRECISION
|
|
1594
1614
|
*/
|
|
1595
1615
|
public getPerpPositionValue(
|
|
@@ -1615,6 +1635,41 @@ export class User {
|
|
|
1615
1635
|
);
|
|
1616
1636
|
}
|
|
1617
1637
|
|
|
1638
|
+
/**
|
|
1639
|
+
* calculates position liabiltiy value in margin system
|
|
1640
|
+
* @returns : Precision QUOTE_PRECISION
|
|
1641
|
+
*/
|
|
1642
|
+
public getPerpLiabilityValue(
|
|
1643
|
+
marketIndex: number,
|
|
1644
|
+
oraclePriceData: OraclePriceData,
|
|
1645
|
+
includeOpenOrders = false
|
|
1646
|
+
): BN {
|
|
1647
|
+
const userPosition =
|
|
1648
|
+
this.getPerpPositionWithLPSettle(
|
|
1649
|
+
marketIndex,
|
|
1650
|
+
undefined,
|
|
1651
|
+
false,
|
|
1652
|
+
true
|
|
1653
|
+
)[0] || this.getEmptyPosition(marketIndex);
|
|
1654
|
+
const market = this.driftClient.getPerpMarketAccount(
|
|
1655
|
+
userPosition.marketIndex
|
|
1656
|
+
);
|
|
1657
|
+
|
|
1658
|
+
if (includeOpenOrders) {
|
|
1659
|
+
return calculateWorstCasePerpLiabilityValue(
|
|
1660
|
+
userPosition,
|
|
1661
|
+
market,
|
|
1662
|
+
oraclePriceData.price
|
|
1663
|
+
).worstCaseLiabilityValue;
|
|
1664
|
+
} else {
|
|
1665
|
+
return calculatePerpLiabilityValue(
|
|
1666
|
+
userPosition.baseAssetAmount,
|
|
1667
|
+
oraclePriceData.price,
|
|
1668
|
+
isVariant(market.contractType, 'prediction')
|
|
1669
|
+
);
|
|
1670
|
+
}
|
|
1671
|
+
}
|
|
1672
|
+
|
|
1618
1673
|
public getPositionSide(
|
|
1619
1674
|
currentPosition: Pick<PerpPosition, 'baseAssetAmount'>
|
|
1620
1675
|
): PositionDirection | undefined {
|
|
@@ -1730,7 +1785,7 @@ export class User {
|
|
|
1730
1785
|
spotAssetValue: BN;
|
|
1731
1786
|
spotLiabilityValue: BN;
|
|
1732
1787
|
} {
|
|
1733
|
-
const perpLiability = this.
|
|
1788
|
+
const perpLiability = this.getTotalPerpPositionLiability(
|
|
1734
1789
|
marginCategory,
|
|
1735
1790
|
undefined,
|
|
1736
1791
|
includeOpenOrders
|
|
@@ -1806,7 +1861,11 @@ export class User {
|
|
|
1806
1861
|
}
|
|
1807
1862
|
|
|
1808
1863
|
getTotalLiabilityValue(marginCategory?: MarginCategory): BN {
|
|
1809
|
-
return this.
|
|
1864
|
+
return this.getTotalPerpPositionLiability(
|
|
1865
|
+
marginCategory,
|
|
1866
|
+
undefined,
|
|
1867
|
+
true
|
|
1868
|
+
).add(
|
|
1810
1869
|
this.getSpotMarketLiabilityValue(
|
|
1811
1870
|
undefined,
|
|
1812
1871
|
marginCategory,
|
|
@@ -2167,6 +2226,8 @@ export class User {
|
|
|
2167
2226
|
const perpMarketWithSameOracle = this.driftClient
|
|
2168
2227
|
.getPerpMarketAccounts()
|
|
2169
2228
|
.find((market) => market.amm.oracle.equals(oracle));
|
|
2229
|
+
const oraclePrice =
|
|
2230
|
+
this.driftClient.getOracleDataForSpotMarket(marketIndex).price;
|
|
2170
2231
|
if (perpMarketWithSameOracle) {
|
|
2171
2232
|
const perpPosition = this.getPerpPositionWithLPSettle(
|
|
2172
2233
|
perpMarketWithSameOracle.marketIndex,
|
|
@@ -2178,7 +2239,8 @@ export class User {
|
|
|
2178
2239
|
this.calculateFreeCollateralDeltaForPerp(
|
|
2179
2240
|
perpMarketWithSameOracle,
|
|
2180
2241
|
perpPosition,
|
|
2181
|
-
ZERO
|
|
2242
|
+
ZERO,
|
|
2243
|
+
oraclePrice
|
|
2182
2244
|
);
|
|
2183
2245
|
|
|
2184
2246
|
freeCollateralDelta = freeCollateralDelta.add(
|
|
@@ -2191,8 +2253,6 @@ export class User {
|
|
|
2191
2253
|
return new BN(-1);
|
|
2192
2254
|
}
|
|
2193
2255
|
|
|
2194
|
-
const oraclePrice =
|
|
2195
|
-
this.driftClient.getOracleDataForSpotMarket(marketIndex).price;
|
|
2196
2256
|
const liqPriceDelta = freeCollateral
|
|
2197
2257
|
.mul(QUOTE_PRECISION)
|
|
2198
2258
|
.div(freeCollateralDelta);
|
|
@@ -2263,6 +2323,7 @@ export class User {
|
|
|
2263
2323
|
market,
|
|
2264
2324
|
currentPerpPosition,
|
|
2265
2325
|
positionBaseSizeChange,
|
|
2326
|
+
oraclePrice,
|
|
2266
2327
|
marginCategory,
|
|
2267
2328
|
includeOpenOrders
|
|
2268
2329
|
);
|
|
@@ -2350,41 +2411,62 @@ export class User {
|
|
|
2350
2411
|
freeCollateralChange = freeCollateralChange.sub(takerFee);
|
|
2351
2412
|
}
|
|
2352
2413
|
|
|
2353
|
-
const
|
|
2354
|
-
|
|
2355
|
-
:
|
|
2414
|
+
const calculateMarginRequirement = (perpPosition: PerpPosition) => {
|
|
2415
|
+
let baseAssetAmount: BN;
|
|
2416
|
+
let liabilityValue: BN;
|
|
2417
|
+
if (includeOpenOrders) {
|
|
2418
|
+
const { worstCaseBaseAssetAmount, worstCaseLiabilityValue } =
|
|
2419
|
+
calculateWorstCasePerpLiabilityValue(
|
|
2420
|
+
perpPosition,
|
|
2421
|
+
market,
|
|
2422
|
+
oraclePrice
|
|
2423
|
+
);
|
|
2424
|
+
baseAssetAmount = worstCaseBaseAssetAmount;
|
|
2425
|
+
liabilityValue = worstCaseLiabilityValue;
|
|
2426
|
+
} else {
|
|
2427
|
+
baseAssetAmount = perpPosition.baseAssetAmount;
|
|
2428
|
+
liabilityValue = calculatePerpLiabilityValue(
|
|
2429
|
+
baseAssetAmount,
|
|
2430
|
+
oraclePrice,
|
|
2431
|
+
isVariant(market.contractType, 'prediction')
|
|
2432
|
+
);
|
|
2433
|
+
}
|
|
2356
2434
|
|
|
2357
|
-
|
|
2435
|
+
const marginRatio = calculateMarketMarginRatio(
|
|
2436
|
+
market,
|
|
2437
|
+
baseAssetAmount.abs(),
|
|
2438
|
+
'Maintenance'
|
|
2439
|
+
);
|
|
2358
2440
|
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2441
|
+
return liabilityValue.mul(new BN(marginRatio)).div(MARGIN_PRECISION);
|
|
2442
|
+
};
|
|
2443
|
+
|
|
2444
|
+
const freeCollateralConsumptionBefore =
|
|
2445
|
+
calculateMarginRequirement(perpPosition);
|
|
2364
2446
|
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
.abs()
|
|
2369
|
-
.sub(baseAssetAmount.abs())
|
|
2370
|
-
.mul(oraclePrice)
|
|
2371
|
-
.div(BASE_PRECISION)
|
|
2372
|
-
.mul(new BN(newMarginRatio))
|
|
2373
|
-
.div(MARGIN_PRECISION)
|
|
2447
|
+
const perpPositionAfter = Object.assign({}, perpPosition);
|
|
2448
|
+
perpPositionAfter.baseAssetAmount = perpPositionAfter.baseAssetAmount.add(
|
|
2449
|
+
positionBaseSizeChange
|
|
2374
2450
|
);
|
|
2375
2451
|
|
|
2376
|
-
|
|
2452
|
+
const freeCollateralConsumptionAfter =
|
|
2453
|
+
calculateMarginRequirement(perpPositionAfter);
|
|
2454
|
+
|
|
2455
|
+
return freeCollateralChange.sub(
|
|
2456
|
+
freeCollateralConsumptionAfter.sub(freeCollateralConsumptionBefore)
|
|
2457
|
+
);
|
|
2377
2458
|
}
|
|
2378
2459
|
|
|
2379
2460
|
calculateFreeCollateralDeltaForPerp(
|
|
2380
2461
|
market: PerpMarketAccount,
|
|
2381
2462
|
perpPosition: PerpPosition,
|
|
2382
2463
|
positionBaseSizeChange: BN,
|
|
2464
|
+
oraclePrice: BN,
|
|
2383
2465
|
marginCategory: MarginCategory = 'Maintenance',
|
|
2384
2466
|
includeOpenOrders = false
|
|
2385
2467
|
): BN | undefined {
|
|
2386
2468
|
const baseAssetAmount = includeOpenOrders
|
|
2387
|
-
? calculateWorstCaseBaseAssetAmount(perpPosition)
|
|
2469
|
+
? calculateWorstCaseBaseAssetAmount(perpPosition, market, oraclePrice)
|
|
2388
2470
|
: perpPosition.baseAssetAmount;
|
|
2389
2471
|
|
|
2390
2472
|
// zero if include orders == false
|
|
@@ -2409,23 +2491,33 @@ export class User {
|
|
|
2409
2491
|
}
|
|
2410
2492
|
|
|
2411
2493
|
let freeCollateralDelta = ZERO;
|
|
2412
|
-
if (
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2494
|
+
if (isVariant(market.contractType, 'prediction')) {
|
|
2495
|
+
// for prediction market, increase in pnl and margin requirement will net out for position
|
|
2496
|
+
// open order margin requirement will change with price though
|
|
2497
|
+
if (orderBaseAssetAmount.gt(ZERO)) {
|
|
2498
|
+
freeCollateralDelta = marginRatioQuotePrecision.neg();
|
|
2499
|
+
} else if (orderBaseAssetAmount.lt(ZERO)) {
|
|
2500
|
+
freeCollateralDelta = marginRatioQuotePrecision;
|
|
2501
|
+
}
|
|
2416
2502
|
} else {
|
|
2417
|
-
|
|
2418
|
-
.sub(marginRatioQuotePrecision)
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2503
|
+
if (proposedBaseAssetAmount.gt(ZERO)) {
|
|
2504
|
+
freeCollateralDelta = QUOTE_PRECISION.sub(marginRatioQuotePrecision)
|
|
2505
|
+
.mul(proposedBaseAssetAmount)
|
|
2506
|
+
.div(BASE_PRECISION);
|
|
2507
|
+
} else {
|
|
2508
|
+
freeCollateralDelta = QUOTE_PRECISION.neg()
|
|
2509
|
+
.sub(marginRatioQuotePrecision)
|
|
2510
|
+
.mul(proposedBaseAssetAmount.abs())
|
|
2511
|
+
.div(BASE_PRECISION);
|
|
2512
|
+
}
|
|
2422
2513
|
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2514
|
+
if (!orderBaseAssetAmount.eq(ZERO)) {
|
|
2515
|
+
freeCollateralDelta = freeCollateralDelta.sub(
|
|
2516
|
+
marginRatioQuotePrecision
|
|
2517
|
+
.mul(orderBaseAssetAmount.abs())
|
|
2518
|
+
.div(BASE_PRECISION)
|
|
2519
|
+
);
|
|
2520
|
+
}
|
|
2429
2521
|
}
|
|
2430
2522
|
|
|
2431
2523
|
return freeCollateralDelta;
|
|
@@ -2519,13 +2611,16 @@ export class User {
|
|
|
2519
2611
|
*
|
|
2520
2612
|
* @param targetMarketIndex
|
|
2521
2613
|
* @param tradeSide
|
|
2522
|
-
* @
|
|
2614
|
+
* @param isLp
|
|
2615
|
+
* @returns { tradeSize: BN, oppositeSideTradeSize: BN} : Precision QUOTE_PRECISION
|
|
2523
2616
|
*/
|
|
2524
2617
|
public getMaxTradeSizeUSDCForPerp(
|
|
2525
2618
|
targetMarketIndex: number,
|
|
2526
2619
|
tradeSide: PositionDirection,
|
|
2527
2620
|
isLp = false
|
|
2528
|
-
): BN {
|
|
2621
|
+
): { tradeSize: BN; oppositeSideTradeSize: BN } {
|
|
2622
|
+
let tradeSize = ZERO;
|
|
2623
|
+
let oppositeSideTradeSize = ZERO;
|
|
2529
2624
|
const currentPosition =
|
|
2530
2625
|
this.getPerpPositionWithLPSettle(targetMarketIndex, undefined, true)[0] ||
|
|
2531
2626
|
this.getEmptyPosition(targetMarketIndex);
|
|
@@ -2552,41 +2647,49 @@ export class User {
|
|
|
2552
2647
|
: ZERO;
|
|
2553
2648
|
|
|
2554
2649
|
// add any position we have on the opposite side of the current trade, because we can "flip" the size of this position without taking any extra leverage.
|
|
2555
|
-
const
|
|
2650
|
+
const oppositeSizeLiabilityValue = targetingSameSide
|
|
2556
2651
|
? ZERO
|
|
2557
|
-
:
|
|
2652
|
+
: calculatePerpLiabilityValue(
|
|
2653
|
+
currentPosition.baseAssetAmount,
|
|
2654
|
+
oracleData.price,
|
|
2655
|
+
isVariant(marketAccount.contractType, 'prediction')
|
|
2656
|
+
);
|
|
2558
2657
|
|
|
2559
|
-
|
|
2658
|
+
const maxPositionSize = this.getPerpBuyingPower(
|
|
2659
|
+
targetMarketIndex,
|
|
2660
|
+
lpBuffer
|
|
2661
|
+
);
|
|
2560
2662
|
|
|
2561
2663
|
if (maxPositionSize.gte(ZERO)) {
|
|
2562
|
-
if (
|
|
2664
|
+
if (oppositeSizeLiabilityValue.eq(ZERO)) {
|
|
2563
2665
|
// case 1 : Regular trade where current total position less than max, and no opposite position to account for
|
|
2564
2666
|
// do nothing
|
|
2667
|
+
tradeSize = maxPositionSize;
|
|
2565
2668
|
} else {
|
|
2566
2669
|
// case 2 : trade where current total position less than max, but need to account for flipping the current position over to the other side
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
);
|
|
2670
|
+
tradeSize = maxPositionSize.add(oppositeSizeLiabilityValue);
|
|
2671
|
+
oppositeSideTradeSize = oppositeSizeLiabilityValue;
|
|
2570
2672
|
}
|
|
2571
2673
|
} else {
|
|
2572
2674
|
// current leverage is greater than max leverage - can only reduce position size
|
|
2573
2675
|
|
|
2574
2676
|
if (!targetingSameSide) {
|
|
2575
2677
|
const market = this.driftClient.getPerpMarketAccount(targetMarketIndex);
|
|
2576
|
-
const
|
|
2577
|
-
|
|
2578
|
-
oracleData
|
|
2678
|
+
const perpLiabilityValue = calculatePerpLiabilityValue(
|
|
2679
|
+
currentPosition.baseAssetAmount,
|
|
2680
|
+
oracleData.price,
|
|
2681
|
+
isVariant(market.contractType, 'prediction')
|
|
2579
2682
|
);
|
|
2580
2683
|
const totalCollateral = this.getTotalCollateral();
|
|
2581
2684
|
const marginRequirement = this.getInitialMarginRequirement();
|
|
2582
|
-
const marginFreedByClosing =
|
|
2685
|
+
const marginFreedByClosing = perpLiabilityValue
|
|
2583
2686
|
.mul(new BN(market.marginRatioInitial))
|
|
2584
2687
|
.div(MARGIN_PRECISION);
|
|
2585
2688
|
const marginRequirementAfterClosing =
|
|
2586
2689
|
marginRequirement.sub(marginFreedByClosing);
|
|
2587
2690
|
|
|
2588
2691
|
if (marginRequirementAfterClosing.gt(totalCollateral)) {
|
|
2589
|
-
|
|
2692
|
+
oppositeSideTradeSize = perpLiabilityValue;
|
|
2590
2693
|
} else {
|
|
2591
2694
|
const freeCollateralAfterClose = totalCollateral.sub(
|
|
2592
2695
|
marginRequirementAfterClosing
|
|
@@ -2598,14 +2701,16 @@ export class User {
|
|
|
2598
2701
|
freeCollateralAfterClose,
|
|
2599
2702
|
ZERO
|
|
2600
2703
|
);
|
|
2601
|
-
|
|
2704
|
+
oppositeSideTradeSize = perpLiabilityValue;
|
|
2705
|
+
tradeSize = buyingPowerAfterClose;
|
|
2602
2706
|
}
|
|
2603
2707
|
} else {
|
|
2604
2708
|
// do nothing if targetting same side
|
|
2709
|
+
tradeSize = maxPositionSize;
|
|
2605
2710
|
}
|
|
2606
2711
|
}
|
|
2607
2712
|
|
|
2608
|
-
return
|
|
2713
|
+
return { tradeSize, oppositeSideTradeSize };
|
|
2609
2714
|
}
|
|
2610
2715
|
|
|
2611
2716
|
/**
|
|
@@ -3235,16 +3340,19 @@ export class User {
|
|
|
3235
3340
|
this.getPerpPositionWithLPSettle(targetMarketIndex)[0] ||
|
|
3236
3341
|
this.getEmptyPosition(targetMarketIndex);
|
|
3237
3342
|
|
|
3343
|
+
const perpMarket = this.driftClient.getPerpMarketAccount(targetMarketIndex);
|
|
3238
3344
|
const oracleData = this.getOracleDataForPerpMarket(targetMarketIndex);
|
|
3239
3345
|
|
|
3240
|
-
let
|
|
3241
|
-
|
|
3242
|
-
|
|
3243
|
-
|
|
3346
|
+
let {
|
|
3347
|
+
// eslint-disable-next-line prefer-const
|
|
3348
|
+
worstCaseBaseAssetAmount: worstCaseBase,
|
|
3349
|
+
worstCaseLiabilityValue: currentPositionQuoteAmount,
|
|
3350
|
+
} = calculateWorstCasePerpLiabilityValue(
|
|
3351
|
+
currentPosition,
|
|
3352
|
+
perpMarket,
|
|
3353
|
+
oracleData.price
|
|
3244
3354
|
);
|
|
3245
3355
|
|
|
3246
|
-
const worstCaseBase = calculateWorstCaseBaseAssetAmount(currentPosition);
|
|
3247
|
-
|
|
3248
3356
|
// current side is short if position base asset amount is negative OR there is no position open but open orders are short
|
|
3249
3357
|
const currentSide =
|
|
3250
3358
|
currentPosition.baseAssetAmount.isNeg() ||
|
|
@@ -3617,8 +3725,14 @@ export class User {
|
|
|
3617
3725
|
perpMarket.marketIndex
|
|
3618
3726
|
);
|
|
3619
3727
|
const oraclePrice = oraclePriceData.price;
|
|
3620
|
-
const
|
|
3621
|
-
|
|
3728
|
+
const {
|
|
3729
|
+
worstCaseBaseAssetAmount: worstCaseBaseAmount,
|
|
3730
|
+
worstCaseLiabilityValue,
|
|
3731
|
+
} = calculateWorstCasePerpLiabilityValue(
|
|
3732
|
+
settledLpPosition,
|
|
3733
|
+
perpMarket,
|
|
3734
|
+
oraclePrice
|
|
3735
|
+
);
|
|
3622
3736
|
|
|
3623
3737
|
const marginRatio = new BN(
|
|
3624
3738
|
calculateMarketMarginRatio(
|
|
@@ -3636,12 +3750,7 @@ export class User {
|
|
|
3636
3750
|
QUOTE_SPOT_MARKET_INDEX
|
|
3637
3751
|
);
|
|
3638
3752
|
|
|
3639
|
-
|
|
3640
|
-
.abs()
|
|
3641
|
-
.mul(oraclePrice)
|
|
3642
|
-
.div(BASE_PRECISION);
|
|
3643
|
-
|
|
3644
|
-
let marginRequirement = baseAssetValue
|
|
3753
|
+
let marginRequirement = worstCaseLiabilityValue
|
|
3645
3754
|
.mul(quoteOraclePriceData.price)
|
|
3646
3755
|
.div(PRICE_PRECISION)
|
|
3647
3756
|
.mul(marginRatio)
|
|
@@ -3667,7 +3776,7 @@ export class User {
|
|
|
3667
3776
|
healthComponents.perpPositions.push({
|
|
3668
3777
|
marketIndex: perpMarket.marketIndex,
|
|
3669
3778
|
size: worstCaseBaseAmount,
|
|
3670
|
-
value:
|
|
3779
|
+
value: worstCaseLiabilityValue,
|
|
3671
3780
|
weight: marginRatio,
|
|
3672
3781
|
weightedValue: marginRequirement,
|
|
3673
3782
|
});
|
|
@@ -3842,14 +3951,14 @@ export class User {
|
|
|
3842
3951
|
|
|
3843
3952
|
let currentPerpPositionValueUSDC = ZERO;
|
|
3844
3953
|
if (currentPerpPosition) {
|
|
3845
|
-
currentPerpPositionValueUSDC = this.
|
|
3954
|
+
currentPerpPositionValueUSDC = this.getPerpLiabilityValue(
|
|
3846
3955
|
marketToIgnore,
|
|
3847
3956
|
oracleData,
|
|
3848
3957
|
includeOpenOrders
|
|
3849
3958
|
);
|
|
3850
3959
|
}
|
|
3851
3960
|
|
|
3852
|
-
return this.
|
|
3961
|
+
return this.getTotalPerpPositionLiability(
|
|
3853
3962
|
marginCategory,
|
|
3854
3963
|
liquidationBuffer,
|
|
3855
3964
|
includeOpenOrders
|