@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.
@@ -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(perpPosition)
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
- if (allBids.abs().gt(allAsks.abs())) {
127
- return allBids;
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 allAsks;
194
+ return baseAssetAmount.abs().mul(oraclePrice).div(BASE_PRECISION);
130
195
  }
131
196
  }
package/src/types.ts CHANGED
@@ -66,6 +66,7 @@ export enum UserStatus {
66
66
  export class ContractType {
67
67
  static readonly PERPETUAL = { perpetual: {} };
68
68
  static readonly FUTURE = { future: {} };
69
+ static readonly PREDICTION = { prediction: {} };
69
70
  }
70
71
 
71
72
  export class ContractTier {
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(perpPosition)
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.getTotalPerpPositionValue(
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
- calculateWeightedPerpPositionValue(
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
- const baseAssetAmount = includeOpenOrders
1473
- ? calculateWorstCaseBaseAssetAmount(perpPosition)
1474
- : perpPosition.baseAssetAmount;
1475
-
1476
- let baseAssetValue = baseAssetAmount
1477
- .abs()
1478
- .mul(valuationPrice)
1479
- .div(BASE_PRECISION);
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
- baseAssetValue = baseAssetValue
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
- baseAssetValue = baseAssetValue.add(
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
- baseAssetValue = baseAssetValue.add(
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 baseAssetValue;
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.calculateWeightedPerpPositionValue(
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
- getTotalPerpPositionValue(
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.calculateWeightedPerpPositionValue(
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 in margin system
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.getTotalPerpPositionValue(
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.getTotalPerpPositionValue(marginCategory, undefined, true).add(
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 baseAssetAmount = includeOpenOrders
2354
- ? calculateWorstCaseBaseAssetAmount(perpPosition)
2355
- : perpPosition.baseAssetAmount;
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
- const newBaseAssetAmount = baseAssetAmount.add(positionBaseSizeChange);
2435
+ const marginRatio = calculateMarketMarginRatio(
2436
+ market,
2437
+ baseAssetAmount.abs(),
2438
+ 'Maintenance'
2439
+ );
2358
2440
 
2359
- const newMarginRatio = calculateMarketMarginRatio(
2360
- market,
2361
- newBaseAssetAmount.abs(),
2362
- 'Maintenance'
2363
- );
2441
+ return liabilityValue.mul(new BN(marginRatio)).div(MARGIN_PRECISION);
2442
+ };
2443
+
2444
+ const freeCollateralConsumptionBefore =
2445
+ calculateMarginRequirement(perpPosition);
2364
2446
 
2365
- // update free collateral to account for new margin requirement from position change
2366
- freeCollateralChange = freeCollateralChange.sub(
2367
- newBaseAssetAmount
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
- return freeCollateralChange;
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 (proposedBaseAssetAmount.gt(ZERO)) {
2413
- freeCollateralDelta = QUOTE_PRECISION.sub(marginRatioQuotePrecision)
2414
- .mul(proposedBaseAssetAmount)
2415
- .div(BASE_PRECISION);
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
- freeCollateralDelta = QUOTE_PRECISION.neg()
2418
- .sub(marginRatioQuotePrecision)
2419
- .mul(proposedBaseAssetAmount.abs())
2420
- .div(BASE_PRECISION);
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
- if (!orderBaseAssetAmount.eq(ZERO)) {
2424
- freeCollateralDelta = freeCollateralDelta.sub(
2425
- marginRatioQuotePrecision
2426
- .mul(orderBaseAssetAmount.abs())
2427
- .div(BASE_PRECISION)
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
- * @returns tradeSizeAllowed : Precision QUOTE_PRECISION
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 oppositeSizeValueUSDC = targetingSameSide
2650
+ const oppositeSizeLiabilityValue = targetingSameSide
2556
2651
  ? ZERO
2557
- : this.getPerpPositionValue(targetMarketIndex, oracleData);
2652
+ : calculatePerpLiabilityValue(
2653
+ currentPosition.baseAssetAmount,
2654
+ oracleData.price,
2655
+ isVariant(marketAccount.contractType, 'prediction')
2656
+ );
2558
2657
 
2559
- let maxPositionSize = this.getPerpBuyingPower(targetMarketIndex, lpBuffer);
2658
+ const maxPositionSize = this.getPerpBuyingPower(
2659
+ targetMarketIndex,
2660
+ lpBuffer
2661
+ );
2560
2662
 
2561
2663
  if (maxPositionSize.gte(ZERO)) {
2562
- if (oppositeSizeValueUSDC.eq(ZERO)) {
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
- maxPositionSize = maxPositionSize.add(
2568
- oppositeSizeValueUSDC.mul(new BN(2))
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 perpPositionValue = this.getPerpPositionValue(
2577
- targetMarketIndex,
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 = perpPositionValue
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
- maxPositionSize = perpPositionValue;
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
- maxPositionSize = perpPositionValue.add(buyingPowerAfterClose);
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 maxPositionSize;
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 currentPositionQuoteAmount = this.getPerpPositionValue(
3241
- targetMarketIndex,
3242
- oracleData,
3243
- includeOpenOrders
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 worstCaseBaseAmount =
3621
- calculateWorstCaseBaseAssetAmount(settledLpPosition);
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
- const baseAssetValue = worstCaseBaseAmount
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: baseAssetValue,
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.getPerpPositionValue(
3954
+ currentPerpPositionValueUSDC = this.getPerpLiabilityValue(
3846
3955
  marketToIgnore,
3847
3956
  oracleData,
3848
3957
  includeOpenOrders
3849
3958
  );
3850
3959
  }
3851
3960
 
3852
- return this.getTotalPerpPositionValue(
3961
+ return this.getTotalPerpPositionLiability(
3853
3962
  marginCategory,
3854
3963
  liquidationBuffer,
3855
3964
  includeOpenOrders