@drift-labs/sdk 0.1.23-master.4 → 0.1.26

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.
Files changed (61) hide show
  1. package/lib/accounts/bulkAccountLoader.d.ts +2 -0
  2. package/lib/accounts/bulkAccountLoader.js +24 -0
  3. package/lib/addresses.js +5 -1
  4. package/lib/admin.d.ts +2 -2
  5. package/lib/admin.js +11 -5
  6. package/lib/clearingHouse.js +7 -1
  7. package/lib/clearingHouseUser.d.ts +12 -17
  8. package/lib/clearingHouseUser.js +114 -217
  9. package/lib/constants/markets.d.ts +6 -4
  10. package/lib/constants/markets.js +48 -0
  11. package/lib/constants/numericConstants.d.ts +2 -2
  12. package/lib/constants/numericConstants.js +3 -3
  13. package/lib/factory/oracleClient.d.ts +5 -0
  14. package/lib/factory/oracleClient.js +16 -0
  15. package/lib/idl/clearing_house.json +58 -5
  16. package/lib/idl/switchboard_v2.json +4663 -0
  17. package/lib/index.d.ts +4 -1
  18. package/lib/index.js +9 -2
  19. package/lib/math/funding.d.ts +6 -6
  20. package/lib/math/funding.js +4 -16
  21. package/lib/math/orders.d.ts +1 -0
  22. package/lib/math/orders.js +19 -1
  23. package/lib/mockUSDCFaucet.js +5 -1
  24. package/lib/oracles/pythClient.d.ts +14 -0
  25. package/lib/oracles/pythClient.js +53 -0
  26. package/lib/oracles/switchboardClient.d.ts +13 -0
  27. package/lib/oracles/switchboardClient.js +76 -0
  28. package/lib/oracles/types.d.ts +15 -0
  29. package/lib/oracles/types.js +2 -0
  30. package/lib/orderParams.d.ts +1 -1
  31. package/lib/orderParams.js +2 -2
  32. package/lib/orders.js +1 -1
  33. package/lib/types.d.ts +5 -1
  34. package/package.json +2 -1
  35. package/src/accounts/bulkAccountLoader.ts +32 -0
  36. package/src/accounts/types.js +10 -0
  37. package/src/accounts/utils.js +7 -0
  38. package/src/accounts/webSocketAccountSubscriber.js +76 -0
  39. package/src/addresses.js +83 -0
  40. package/src/admin.ts +15 -4
  41. package/src/clearingHouse.ts +2 -0
  42. package/src/clearingHouseUser.ts +161 -330
  43. package/src/constants/markets.ts +54 -3
  44. package/src/constants/numericConstants.ts +2 -2
  45. package/src/factory/oracleClient.ts +22 -0
  46. package/src/idl/clearing_house.json +58 -5
  47. package/src/idl/switchboard_v2.json +4663 -0
  48. package/src/index.ts +4 -1
  49. package/src/math/funding.ts +9 -25
  50. package/src/math/orders.ts +28 -0
  51. package/src/mockUSDCFaucet.js +171 -0
  52. package/src/oracles/pythClient.ts +49 -0
  53. package/src/oracles/switchboardClient.ts +87 -0
  54. package/src/oracles/types.ts +15 -0
  55. package/src/orderParams.ts +3 -2
  56. package/src/orders.ts +1 -1
  57. package/src/types.js +60 -0
  58. package/src/types.ts +6 -1
  59. package/lib/pythClient.d.ts +0 -7
  60. package/lib/pythClient.js +0 -25
  61. package/src/pythClient.ts +0 -15
@@ -3,24 +3,25 @@ import { EventEmitter } from 'events';
3
3
  import StrictEventEmitter from 'strict-event-emitter-types';
4
4
  import { ClearingHouse } from './clearingHouse';
5
5
  import {
6
+ isVariant,
7
+ MarginCategory,
6
8
  Order,
7
9
  UserAccount,
8
10
  UserOrdersAccount,
9
11
  UserPosition,
10
12
  UserPositionsAccount,
11
13
  } from './types';
12
- import { calculateEntryPrice, isEmptyPosition } from './math/position';
14
+ import { calculateEntryPrice } from './math/position';
13
15
  import {
14
16
  MARK_PRICE_PRECISION,
15
17
  AMM_TO_QUOTE_PRECISION_RATIO,
16
18
  ZERO,
17
19
  TEN_THOUSAND,
18
20
  BN_MAX,
19
- PARTIAL_LIQUIDATION_RATIO,
20
- FULL_LIQUIDATION_RATIO,
21
21
  QUOTE_PRECISION,
22
22
  AMM_RESERVE_PRECISION,
23
23
  PRICE_TO_QUOTE_PRECISION,
24
+ MARGIN_PRECISION,
24
25
  } from './constants/numericConstants';
25
26
  import { UserAccountSubscriber, UserAccountEvents } from './accounts/types';
26
27
  import {
@@ -30,7 +31,6 @@ import {
30
31
  calculatePositionPNL,
31
32
  PositionDirection,
32
33
  getUserOrdersAccountPublicKey,
33
- calculateNewStateAfterOrder,
34
34
  calculateTradeSlippage,
35
35
  BN,
36
36
  } from '.';
@@ -202,21 +202,51 @@ export class ClearingHouseUser {
202
202
  * calculates Buying Power = FC * MAX_LEVERAGE
203
203
  * @returns : Precision QUOTE_PRECISION
204
204
  */
205
- public getBuyingPower(): BN {
205
+ public getBuyingPower(marketIndex: BN | number): BN {
206
206
  return this.getFreeCollateral()
207
- .mul(this.getMaxLeverage('Initial'))
207
+ .mul(this.getMaxLeverage(marketIndex, 'Initial'))
208
208
  .div(TEN_THOUSAND);
209
209
  }
210
210
 
211
211
  /**
212
- * calculates Free Collateral = (TC - TPV) * MAX_LEVERAGE
212
+ * calculates Free Collateral = Total collateral - initial margin requirement
213
213
  * @returns : Precision QUOTE_PRECISION
214
214
  */
215
215
  public getFreeCollateral(): BN {
216
- return this.getTotalCollateral().sub(
217
- this.getTotalPositionValue()
218
- .mul(TEN_THOUSAND)
219
- .div(this.getMaxLeverage('Initial'))
216
+ const totalCollateral = this.getTotalCollateral();
217
+ const initialMarginRequirement = this.getInitialMarginRequirement();
218
+ const freeCollateral = totalCollateral.sub(initialMarginRequirement);
219
+ return freeCollateral.gte(ZERO) ? freeCollateral : ZERO;
220
+ }
221
+
222
+ public getInitialMarginRequirement(): BN {
223
+ return this.getUserPositionsAccount().positions.reduce(
224
+ (marginRequirement, marketPosition) => {
225
+ const market = this.clearingHouse.getMarket(marketPosition.marketIndex);
226
+ return marginRequirement.add(
227
+ calculateBaseAssetValue(market, marketPosition)
228
+ .mul(new BN(market.marginRatioInitial))
229
+ .div(MARGIN_PRECISION)
230
+ );
231
+ },
232
+ ZERO
233
+ );
234
+ }
235
+
236
+ /**
237
+ * @returns The partial margin requirement in USDC. : QUOTE_PRECISION
238
+ */
239
+ public getPartialMarginRequirement(): BN {
240
+ return this.getUserPositionsAccount().positions.reduce(
241
+ (marginRequirement, marketPosition) => {
242
+ const market = this.clearingHouse.getMarket(marketPosition.marketIndex);
243
+ return marginRequirement.add(
244
+ calculateBaseAssetValue(market, marketPosition)
245
+ .mul(new BN(market.marginRatioPartial))
246
+ .div(MARGIN_PRECISION)
247
+ );
248
+ },
249
+ ZERO
220
250
  );
221
251
  }
222
252
 
@@ -363,25 +393,30 @@ export class ClearingHouseUser {
363
393
  * @params category {Initial, Partial, Maintenance}
364
394
  * @returns : Precision TEN_THOUSAND
365
395
  */
366
- public getMaxLeverage(category?: 'Initial' | 'Partial' | 'Maintenance'): BN {
367
- const chState = this.clearingHouse.getStateAccount();
368
- let marginRatioCategory: BN;
396
+ public getMaxLeverage(
397
+ marketIndex: BN | number,
398
+ category: MarginCategory = 'Initial'
399
+ ): BN {
400
+ const market = this.clearingHouse.getMarket(marketIndex);
401
+ let marginRatioCategory: number;
369
402
 
370
403
  switch (category) {
371
404
  case 'Initial':
372
- marginRatioCategory = chState.marginRatioInitial;
405
+ marginRatioCategory = market.marginRatioInitial;
373
406
  break;
374
407
  case 'Maintenance':
375
- marginRatioCategory = chState.marginRatioMaintenance;
408
+ marginRatioCategory = market.marginRatioMaintenance;
376
409
  break;
377
410
  case 'Partial':
378
- marginRatioCategory = chState.marginRatioPartial;
411
+ marginRatioCategory = market.marginRatioPartial;
379
412
  break;
380
413
  default:
381
- marginRatioCategory = chState.marginRatioInitial;
414
+ marginRatioCategory = market.marginRatioInitial;
382
415
  break;
383
416
  }
384
- const maxLeverage = TEN_THOUSAND.mul(TEN_THOUSAND).div(marginRatioCategory);
417
+ const maxLeverage = TEN_THOUSAND.mul(TEN_THOUSAND).div(
418
+ new BN(marginRatioCategory)
419
+ );
385
420
  return maxLeverage;
386
421
  }
387
422
 
@@ -400,8 +435,10 @@ export class ClearingHouseUser {
400
435
  }
401
436
 
402
437
  public canBeLiquidated(): [boolean, BN] {
438
+ const totalCollateral = this.getTotalCollateral();
439
+ const partialMaintenanceRequirement = this.getPartialMarginRequirement();
403
440
  const marginRatio = this.getMarginRatio();
404
- const canLiquidate = marginRatio.lte(PARTIAL_LIQUIDATION_RATIO);
441
+ const canLiquidate = totalCollateral.lt(partialMaintenanceRequirement);
405
442
  return [canLiquidate, marginRatio];
406
443
  }
407
444
 
@@ -436,143 +473,17 @@ export class ClearingHouseUser {
436
473
 
437
474
  /**
438
475
  * Calculate the liquidation price of a position, with optional parameter to calculate the liquidation price after a trade
439
- * @param targetMarket
440
- * @param positionBaseSizeChange // change in position size to calculate liquidation price for : Precision 10^13
441
- * @param partial
442
- * @returns Precision : MARK_PRICE_PRECISION
443
- */
444
- public liquidationPriceOld(
445
- targetMarket: Pick<UserPosition, 'marketIndex'>,
446
- positionBaseSizeChange: BN = ZERO,
447
- partial = false
448
- ): BN {
449
- // +/-(margin_ratio-liq_ratio) * price_now = price_liq
450
- // todo: margin_ratio is not symmetric on price action (both numer and denom change)
451
- // margin_ratio = collateral / base_asset_value
452
-
453
- /* example: assume BTC price is $40k (examine 10% up/down)
454
-
455
- if 10k deposit and levered 10x short BTC => BTC up $400 means:
456
- 1. higher base_asset_value (+$4k)
457
- 2. lower collateral (-$4k)
458
- 3. (10k - 4k)/(100k + 4k) => 6k/104k => .0576
459
-
460
- for 10x long, BTC down $400:
461
- 3. (10k - 4k) / (100k - 4k) = 6k/96k => .0625 */
462
-
463
- const currentPrice = calculateMarkPrice(
464
- this.clearingHouse.getMarket(targetMarket.marketIndex)
465
- );
466
-
467
- const totalCollateralUSDC = this.getTotalCollateral();
468
-
469
- // calculate the total position value ignoring any value from the target market of the trade
470
- const totalCurrentPositionValueIgnoringTargetUSDC =
471
- this.getTotalPositionValueExcludingMarket(targetMarket.marketIndex);
472
-
473
- const currentMarketPosition =
474
- this.getUserPosition(targetMarket.marketIndex) ||
475
- this.getEmptyPosition(targetMarket.marketIndex);
476
-
477
- const currentMarketPositionBaseSize = currentMarketPosition.baseAssetAmount;
478
-
479
- // calculate position for current market after trade
480
- const proposedMarketPosition: UserPosition = {
481
- marketIndex: targetMarket.marketIndex,
482
- baseAssetAmount: currentMarketPositionBaseSize.add(
483
- positionBaseSizeChange
484
- ),
485
- lastCumulativeFundingRate: new BN(0),
486
- quoteAssetAmount: new BN(0),
487
- openOrders: new BN(0),
488
- };
489
-
490
- const market = this.clearingHouse.getMarket(
491
- proposedMarketPosition.marketIndex
492
- );
493
-
494
- const proposedMarketPositionValueUSDC = calculateBaseAssetValue(
495
- market,
496
- proposedMarketPosition
497
- );
498
-
499
- // total position value after trade
500
- const targetTotalPositionValueUSDC =
501
- totalCurrentPositionValueIgnoringTargetUSDC.add(
502
- proposedMarketPositionValueUSDC
503
- );
504
-
505
- let totalFreeCollateralUSDC = this.getTotalCollateral().sub(
506
- this.getTotalPositionValue()
507
- .mul(TEN_THOUSAND)
508
- .div(this.getMaxLeverage('Maintenance'))
509
- );
510
-
511
- if (partial) {
512
- totalFreeCollateralUSDC = this.getTotalCollateral().sub(
513
- this.getTotalPositionValue()
514
- .mul(TEN_THOUSAND)
515
- .div(this.getMaxLeverage('Partial'))
516
- );
517
- }
518
-
519
- // if the position value after the trade is less than total collateral, there is no liq price
520
- if (
521
- targetTotalPositionValueUSDC.lte(totalFreeCollateralUSDC) &&
522
- proposedMarketPosition.baseAssetAmount.gt(ZERO)
523
- ) {
524
- return new BN(-1);
525
- }
526
-
527
- // get current margin ratio based on current collateral and proposed total position value
528
- let marginRatio;
529
- if (proposedMarketPositionValueUSDC.eq(ZERO)) {
530
- marginRatio = BN_MAX;
531
- } else {
532
- marginRatio = totalCollateralUSDC
533
- .mul(TEN_THOUSAND)
534
- .div(proposedMarketPositionValueUSDC);
535
- }
536
-
537
- let liqRatio = FULL_LIQUIDATION_RATIO;
538
- if (partial) {
539
- liqRatio = PARTIAL_LIQUIDATION_RATIO;
540
- }
541
-
542
- // sign of position in current market after the trade
543
- const baseAssetSignIsNeg = proposedMarketPosition.baseAssetAmount.isNeg();
544
-
545
- let pctChange = marginRatio.abs().sub(liqRatio);
546
- // if user is short, higher price is liq
547
- if (baseAssetSignIsNeg) {
548
- pctChange = pctChange.add(TEN_THOUSAND);
549
- } else {
550
- if (TEN_THOUSAND.lte(pctChange)) {
551
- // no liquidation price, position is a fully/over collateralized long
552
- // handle as NaN on UI
553
- return new BN(-1);
554
- }
555
- pctChange = TEN_THOUSAND.sub(pctChange);
556
- }
557
-
558
- const liqPrice = currentPrice.mul(pctChange).div(TEN_THOUSAND);
559
-
560
- return liqPrice;
561
- }
562
-
563
- /**
564
- * Calculate the liquidation price of a position, with optional parameter to calculate the liquidation price after a trade
565
- * @param targetMarket
476
+ * @param marketPosition
566
477
  * @param positionBaseSizeChange // change in position size to calculate liquidation price for : Precision 10^13
567
478
  * @param partial
568
479
  * @returns Precision : MARK_PRICE_PRECISION
569
480
  */
570
481
  public liquidationPrice(
571
- targetMarket: Pick<UserPosition, 'marketIndex'>,
482
+ marketPosition: Pick<UserPosition, 'marketIndex'>,
572
483
  positionBaseSizeChange: BN = ZERO,
573
484
  partial = false
574
485
  ): BN {
575
- // solves formula for example calc below
486
+ // solves formula for example canBeLiquidated below
576
487
 
577
488
  /* example: assume BTC price is $40k (examine 10% up/down)
578
489
 
@@ -584,21 +495,15 @@ export class ClearingHouseUser {
584
495
  for 10x long, BTC down $400:
585
496
  3. (10k - 4k) / (100k - 4k) = 6k/96k => .0625 */
586
497
 
587
- const tc = this.getTotalCollateral();
588
- const tpv = this.getTotalPositionValue();
589
-
590
- const partialLev = 16;
591
- const maintLev = 20;
592
-
593
- const thisLev = partial ? new BN(partialLev) : new BN(maintLev);
498
+ const totalCollateral = this.getTotalCollateral();
594
499
 
595
500
  // calculate the total position value ignoring any value from the target market of the trade
596
- const totalCurrentPositionValueIgnoringTargetUSDC =
597
- this.getTotalPositionValueExcludingMarket(targetMarket.marketIndex);
501
+ const totalPositionValueExcludingTargetMarket =
502
+ this.getTotalPositionValueExcludingMarket(marketPosition.marketIndex);
598
503
 
599
504
  const currentMarketPosition =
600
- this.getUserPosition(targetMarket.marketIndex) ||
601
- this.getEmptyPosition(targetMarket.marketIndex);
505
+ this.getUserPosition(marketPosition.marketIndex) ||
506
+ this.getEmptyPosition(marketPosition.marketIndex);
602
507
 
603
508
  const currentMarketPositionBaseSize = currentMarketPosition.baseAssetAmount;
604
509
 
@@ -608,7 +513,7 @@ export class ClearingHouseUser {
608
513
 
609
514
  // calculate position for current market after trade
610
515
  const proposedMarketPosition: UserPosition = {
611
- marketIndex: targetMarket.marketIndex,
516
+ marketIndex: marketPosition.marketIndex,
612
517
  baseAssetAmount: proposedBaseAssetAmount,
613
518
  lastCumulativeFundingRate:
614
519
  currentMarketPosition.lastCumulativeFundingRate,
@@ -616,87 +521,112 @@ export class ClearingHouseUser {
616
521
  openOrders: new BN(0),
617
522
  };
618
523
 
524
+ if (proposedBaseAssetAmount.eq(ZERO)) return new BN(-1);
525
+
619
526
  const market = this.clearingHouse.getMarket(
620
527
  proposedMarketPosition.marketIndex
621
528
  );
622
529
 
623
- const proposedMarketPositionValueUSDC = calculateBaseAssetValue(
530
+ const proposedMarketPositionValue = calculateBaseAssetValue(
624
531
  market,
625
532
  proposedMarketPosition
626
533
  );
627
534
 
628
535
  // total position value after trade
629
- const targetTotalPositionValueUSDC =
630
- totalCurrentPositionValueIgnoringTargetUSDC.add(
631
- proposedMarketPositionValueUSDC
536
+ const totalPositionValueAfterTrade =
537
+ totalPositionValueExcludingTargetMarket.add(proposedMarketPositionValue);
538
+
539
+ const marginRequirementExcludingTargetMarket =
540
+ this.getUserPositionsAccount().positions.reduce(
541
+ (totalMarginRequirement, position) => {
542
+ if (!position.marketIndex.eq(marketPosition.marketIndex)) {
543
+ const market = this.clearingHouse.getMarket(position.marketIndex);
544
+ const positionValue = calculateBaseAssetValue(market, position);
545
+ const marketMarginRequirement = positionValue
546
+ .mul(
547
+ partial
548
+ ? new BN(market.marginRatioPartial)
549
+ : new BN(market.marginRatioMaintenance)
550
+ )
551
+ .div(MARGIN_PRECISION);
552
+ totalMarginRequirement = totalMarginRequirement.add(
553
+ marketMarginRequirement
554
+ );
555
+ }
556
+ return totalMarginRequirement;
557
+ },
558
+ ZERO
632
559
  );
633
560
 
634
- let totalFreeCollateralUSDC = tc.sub(
635
- totalCurrentPositionValueIgnoringTargetUSDC
636
- .mul(TEN_THOUSAND)
637
- .div(this.getMaxLeverage('Maintenance'))
561
+ const freeCollateralExcludingTargetMarket = totalCollateral.sub(
562
+ marginRequirementExcludingTargetMarket
638
563
  );
639
564
 
640
- if (partial) {
641
- totalFreeCollateralUSDC = tc.sub(
642
- totalCurrentPositionValueIgnoringTargetUSDC
643
- .mul(TEN_THOUSAND)
644
- .div(this.getMaxLeverage('Partial'))
645
- );
565
+ // if the position value after the trade is less than free collateral, there is no liq price
566
+ if (
567
+ totalPositionValueAfterTrade.lte(freeCollateralExcludingTargetMarket) &&
568
+ proposedMarketPosition.baseAssetAmount.abs().gt(ZERO)
569
+ ) {
570
+ return new BN(-1);
646
571
  }
647
572
 
648
- let priceDelt;
573
+ const marginRequirementAfterTrade =
574
+ marginRequirementExcludingTargetMarket.add(
575
+ proposedMarketPositionValue
576
+ .mul(
577
+ partial
578
+ ? new BN(market.marginRatioPartial)
579
+ : new BN(market.marginRatioMaintenance)
580
+ )
581
+ .div(MARGIN_PRECISION)
582
+ );
583
+ const freeCollateralAfterTrade = totalCollateral.sub(
584
+ marginRequirementAfterTrade
585
+ );
586
+
587
+ const marketMaxLeverage = partial
588
+ ? this.getMaxLeverage(proposedMarketPosition.marketIndex, 'Partial')
589
+ : this.getMaxLeverage(proposedMarketPosition.marketIndex, 'Maintenance');
590
+
591
+ let priceDelta;
649
592
  if (proposedBaseAssetAmount.lt(ZERO)) {
650
- priceDelt = tc
651
- .mul(thisLev)
652
- .sub(tpv)
593
+ priceDelta = freeCollateralAfterTrade
594
+ .mul(marketMaxLeverage) // precision is TEN_THOUSAND
595
+ .div(marketMaxLeverage.add(TEN_THOUSAND))
653
596
  .mul(PRICE_TO_QUOTE_PRECISION)
654
- .div(thisLev.add(new BN(1)));
597
+ .mul(AMM_RESERVE_PRECISION)
598
+ .div(proposedBaseAssetAmount);
655
599
  } else {
656
- priceDelt = tc
657
- .mul(thisLev)
658
- .sub(tpv)
600
+ priceDelta = freeCollateralAfterTrade
601
+ .mul(marketMaxLeverage) // precision is TEN_THOUSAND
602
+ .div(marketMaxLeverage.sub(TEN_THOUSAND))
659
603
  .mul(PRICE_TO_QUOTE_PRECISION)
660
- .div(thisLev.sub(new BN(1)));
604
+ .mul(AMM_RESERVE_PRECISION)
605
+ .div(proposedBaseAssetAmount);
661
606
  }
662
607
 
663
- let currentPrice;
608
+ let markPriceAfterTrade;
664
609
  if (positionBaseSizeChange.eq(ZERO)) {
665
- currentPrice = calculateMarkPrice(
666
- this.clearingHouse.getMarket(targetMarket.marketIndex)
610
+ markPriceAfterTrade = calculateMarkPrice(
611
+ this.clearingHouse.getMarket(marketPosition.marketIndex)
667
612
  );
668
613
  } else {
669
614
  const direction = positionBaseSizeChange.gt(ZERO)
670
615
  ? PositionDirection.LONG
671
616
  : PositionDirection.SHORT;
672
- currentPrice = calculateTradeSlippage(
617
+ markPriceAfterTrade = calculateTradeSlippage(
673
618
  direction,
674
619
  positionBaseSizeChange.abs(),
675
- this.clearingHouse.getMarket(targetMarket.marketIndex),
620
+ this.clearingHouse.getMarket(marketPosition.marketIndex),
676
621
  'base'
677
622
  )[3]; // newPrice after swap
678
623
  }
679
624
 
680
- // if the position value after the trade is less than total collateral, there is no liq price
681
- if (
682
- targetTotalPositionValueUSDC.lte(totalFreeCollateralUSDC) &&
683
- proposedMarketPosition.baseAssetAmount.gt(ZERO)
684
- ) {
685
- return new BN(-1);
686
- }
687
-
688
- if (proposedBaseAssetAmount.eq(ZERO)) return new BN(-1);
689
-
690
- const eatMargin2 = priceDelt
691
- .mul(AMM_RESERVE_PRECISION)
692
- .div(proposedBaseAssetAmount);
693
-
694
- if (eatMargin2.gt(currentPrice)) {
625
+ if (priceDelta.gt(markPriceAfterTrade)) {
695
626
  return new BN(-1);
696
627
  }
697
628
 
698
- const liqPrice = currentPrice.sub(eatMargin2);
699
- return liqPrice;
629
+ return markPriceAfterTrade.sub(priceDelta);
700
630
  }
701
631
 
702
632
  /**
@@ -750,50 +680,33 @@ export class ClearingHouseUser {
750
680
  *
751
681
  * @param targetMarketIndex
752
682
  * @param tradeSide
753
- * @param userMaxLeverageSetting - leverage : Precision TEN_THOUSAND
754
683
  * @returns tradeSizeAllowed : Precision QUOTE_PRECISION
755
684
  */
756
685
  public getMaxTradeSizeUSDC(
757
686
  targetMarketIndex: BN,
758
- tradeSide: PositionDirection,
759
- userMaxLeverageSetting: BN
687
+ tradeSide: PositionDirection
760
688
  ): BN {
761
689
  const currentPosition =
762
690
  this.getUserPosition(targetMarketIndex) ||
763
691
  this.getEmptyPosition(targetMarketIndex);
764
692
 
765
- const targetSide = tradeSide === PositionDirection.SHORT ? 'short' : 'long';
693
+ const targetSide = isVariant(tradeSide, 'short') ? 'short' : 'long';
766
694
 
767
695
  const currentPositionSide = currentPosition?.baseAssetAmount.isNeg()
768
696
  ? 'short'
769
697
  : 'long';
770
698
 
771
- const targettingSameSide = !currentPosition
699
+ const targetingSameSide = !currentPosition
772
700
  ? true
773
701
  : targetSide === currentPositionSide;
774
702
 
775
703
  // 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.
776
- const oppositeSizeValueUSDC = targettingSameSide
704
+ const oppositeSizeValueUSDC = targetingSameSide
777
705
  ? ZERO
778
706
  : this.getPositionValue(targetMarketIndex);
779
707
 
780
- // get current leverage
781
- const currentLeverage = this.getLeverage();
782
-
783
- const remainingLeverage = BN.max(
784
- userMaxLeverageSetting.sub(currentLeverage),
785
- ZERO
786
- );
787
-
788
- // get total collateral
789
- const totalCollateral = this.getTotalCollateral();
790
-
791
- // position side allowed based purely on current leverage
792
- let maxPositionSize = remainingLeverage
793
- .mul(totalCollateral)
794
- .div(TEN_THOUSAND);
795
-
796
- if (userMaxLeverageSetting.sub(currentLeverage).gte(ZERO)) {
708
+ let maxPositionSize = this.getBuyingPower(targetMarketIndex);
709
+ if (maxPositionSize.gte(ZERO)) {
797
710
  if (oppositeSizeValueUSDC.eq(ZERO)) {
798
711
  // case 1 : Regular trade where current total position less than max, and no opposite position to account for
799
712
  // do nothing
@@ -806,39 +719,27 @@ export class ClearingHouseUser {
806
719
  } else {
807
720
  // current leverage is greater than max leverage - can only reduce position size
808
721
 
809
- if (!targettingSameSide) {
810
- const currentPositionQuoteSize =
811
- this.getPositionValue(targetMarketIndex);
812
-
813
- const currentTotalQuoteSize = currentLeverage
814
- .mul(totalCollateral)
815
- .div(TEN_THOUSAND);
816
-
817
- const otherPositionsTotalQuoteSize = currentTotalQuoteSize.sub(
818
- currentPositionQuoteSize
819
- );
820
-
821
- const quoteValueOfMaxLeverage = userMaxLeverageSetting
822
- .mul(totalCollateral)
823
- .div(TEN_THOUSAND);
824
-
825
- if (
826
- otherPositionsTotalQuoteSize
827
- .sub(currentPositionQuoteSize)
828
- .gte(quoteValueOfMaxLeverage)
829
- ) {
830
- // case 3: Can only reduce the current position because it will still be greater than max leverage
831
-
832
- maxPositionSize = currentPositionQuoteSize;
722
+ if (!targetingSameSide) {
723
+ const market = this.clearingHouse.getMarket(targetMarketIndex);
724
+ const marketPositionValue = this.getPositionValue(targetMarketIndex);
725
+ const totalCollateral = this.getTotalCollateral();
726
+ const marginRequirement = this.getInitialMarginRequirement();
727
+ const marginFreedByClosing = marketPositionValue
728
+ .mul(new BN(market.marginRatioInitial))
729
+ .div(MARGIN_PRECISION);
730
+ const marginRequirementAfterClosing =
731
+ marginRequirement.sub(marginFreedByClosing);
732
+
733
+ if (marginRequirementAfterClosing.gt(totalCollateral)) {
734
+ maxPositionSize = marketPositionValue;
833
735
  } else {
834
- // case 4: Can reduce the position, and then take extra remaining quote to get to max leverage
835
-
836
- const allowedQuoteSizeAfterClosingCurrentPosition =
837
- quoteValueOfMaxLeverage.sub(otherPositionsTotalQuoteSize);
838
-
839
- maxPositionSize = currentPositionQuoteSize.add(
840
- allowedQuoteSizeAfterClosingCurrentPosition
736
+ const freeCollateralAfterClose = totalCollateral.sub(
737
+ marginRequirementAfterClosing
841
738
  );
739
+ const buyingPowerAfterClose = freeCollateralAfterClose
740
+ .mul(this.getMaxLeverage(targetMarketIndex))
741
+ .div(TEN_THOUSAND);
742
+ maxPositionSize = marketPositionValue.add(buyingPowerAfterClose);
842
743
  }
843
744
  } else {
844
745
  // do nothing if targetting same side
@@ -932,74 +833,4 @@ export class ClearingHouseUser {
932
833
 
933
834
  return this.getTotalPositionValue().sub(currentMarketPositionValueUSDC);
934
835
  }
935
-
936
- public canFillOrder(order: Order): boolean {
937
- const userAccount = this.getUserAccount();
938
- const userPositionsAccount = this.getUserPositionsAccount();
939
- const userPosition = this.getUserPosition(order.marketIndex);
940
- const market = this.clearingHouse.getMarket(order.marketIndex);
941
-
942
- if (isEmptyPosition(userPosition)) {
943
- return false;
944
- }
945
-
946
- const newState = calculateNewStateAfterOrder(
947
- userAccount,
948
- userPosition,
949
- market,
950
- order
951
- );
952
- if (newState === null) {
953
- return false;
954
- }
955
- const [userAccountAfter, userPositionAfter, marketAfter] = newState;
956
-
957
- const totalPositionValue = userPositionsAccount.positions.reduce(
958
- (positionValue, marketPosition) => {
959
- let market = this.clearingHouse.getMarket(marketPosition.marketIndex);
960
- if (marketPosition.marketIndex.eq(order.marketIndex)) {
961
- market = marketAfter;
962
- marketPosition = userPositionAfter;
963
- }
964
-
965
- return positionValue.add(
966
- calculateBaseAssetValue(market, marketPosition)
967
- );
968
- },
969
- ZERO
970
- );
971
-
972
- if (totalPositionValue.eq(ZERO)) {
973
- return true;
974
- }
975
-
976
- const unrealizedPnL = userPositionsAccount.positions.reduce(
977
- (pnl, marketPosition) => {
978
- let market = this.clearingHouse.getMarket(marketPosition.marketIndex);
979
- pnl = pnl.add(
980
- calculatePositionFundingPNL(market, marketPosition).div(
981
- PRICE_TO_QUOTE_PRECISION
982
- )
983
- );
984
-
985
- if (marketPosition.marketIndex.eq(order.marketIndex)) {
986
- market = marketAfter;
987
- marketPosition = userPositionAfter;
988
- }
989
-
990
- // update
991
- return pnl.add(calculatePositionPNL(market, marketPosition, false));
992
- },
993
- ZERO
994
- );
995
- const totalCollateral = userAccountAfter.collateral.add(unrealizedPnL);
996
-
997
- const marginRatioAfter = totalCollateral
998
- .mul(TEN_THOUSAND)
999
- .div(totalPositionValue);
1000
-
1001
- const marginRatioInitial =
1002
- this.clearingHouse.getStateAccount().marginRatioInitial;
1003
- return marginRatioAfter.gte(marginRatioInitial);
1004
- }
1005
836
  }