@drift-labs/sdk 0.1.21-master.3 → 0.1.21-master.4

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.
@@ -146,7 +146,22 @@ export declare class ClearingHouseUser {
146
146
  liquidationPriceAfterClose(positionMarketIndex: BN, closeQuoteAmount: BN): BN;
147
147
  /**
148
148
  * Get the maximum trade size for a given market, taking into account the user's current leverage, positions, collateral, etc.
149
- * @param marketIndex
149
+ *
150
+ * To Calculate Max Quote Available:
151
+ *
152
+ * Case 1: SameSide
153
+ * => Remaining quote to get to maxLeverage
154
+ *
155
+ * Case 2: NOT SameSide && currentLeverage <= maxLeverage
156
+ * => Current opposite position x2 + remaining to get to maxLeverage
157
+ *
158
+ * Case 3: NOT SameSide && currentLeverage > maxLeverage && otherPositions - currentPosition > maxLeverage
159
+ * => strictly reduce current position size
160
+ *
161
+ * Case 4: NOT SameSide && currentLeverage > maxLeverage && otherPositions - currentPosition < maxLeverage
162
+ * => current position + remaining to get to maxLeverage
163
+ *
164
+ * @param targetMarketIndex
150
165
  * @param tradeSide
151
166
  * @param userMaxLeverageSetting - leverage : Precision TEN_THOUSAND
152
167
  * @returns tradeSizeAllowed : Precision QUOTE_PRECISION
@@ -513,32 +513,42 @@ class ClearingHouseUser {
513
513
  }
514
514
  /**
515
515
  * Get the maximum trade size for a given market, taking into account the user's current leverage, positions, collateral, etc.
516
- * @param marketIndex
516
+ *
517
+ * To Calculate Max Quote Available:
518
+ *
519
+ * Case 1: SameSide
520
+ * => Remaining quote to get to maxLeverage
521
+ *
522
+ * Case 2: NOT SameSide && currentLeverage <= maxLeverage
523
+ * => Current opposite position x2 + remaining to get to maxLeverage
524
+ *
525
+ * Case 3: NOT SameSide && currentLeverage > maxLeverage && otherPositions - currentPosition > maxLeverage
526
+ * => strictly reduce current position size
527
+ *
528
+ * Case 4: NOT SameSide && currentLeverage > maxLeverage && otherPositions - currentPosition < maxLeverage
529
+ * => current position + remaining to get to maxLeverage
530
+ *
531
+ * @param targetMarketIndex
517
532
  * @param tradeSide
518
533
  * @param userMaxLeverageSetting - leverage : Precision TEN_THOUSAND
519
534
  * @returns tradeSizeAllowed : Precision QUOTE_PRECISION
520
535
  */
521
536
  getMaxTradeSizeUSDC(targetMarketIndex, tradeSide, userMaxLeverageSetting) {
522
- // inline function which get's the current position size on the opposite side of the target trade
523
- const getOppositePositionValueUSDC = () => {
524
- if (!currentPosition)
525
- return numericConstants_1.ZERO;
526
- const side = tradeSide === _1.PositionDirection.SHORT ? 'short' : 'long';
527
- if (side === 'long' && (currentPosition === null || currentPosition === void 0 ? void 0 : currentPosition.baseAssetAmount.isNeg())) {
528
- return this.getPositionValue(targetMarketIndex);
529
- }
530
- else if (side === 'short' &&
531
- !(currentPosition === null || currentPosition === void 0 ? void 0 : currentPosition.baseAssetAmount.isNeg())) {
532
- return this.getPositionValue(targetMarketIndex);
533
- }
534
- return numericConstants_1.ZERO;
535
- };
536
537
  const currentPosition = this.getUserPosition(targetMarketIndex) ||
537
538
  this.getEmptyPosition(targetMarketIndex);
539
+ const targetSide = tradeSide === _1.PositionDirection.SHORT ? 'short' : 'long';
540
+ const currentPositionSide = (currentPosition === null || currentPosition === void 0 ? void 0 : currentPosition.baseAssetAmount.isNeg())
541
+ ? 'short'
542
+ : 'long';
543
+ const targettingSameSide = !currentPosition
544
+ ? true
545
+ : targetSide === currentPositionSide;
546
+ // 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.
547
+ const oppositeSizeValueUSDC = targettingSameSide
548
+ ? numericConstants_1.ZERO
549
+ : this.getPositionValue(targetMarketIndex);
538
550
  // get current leverage
539
551
  const currentLeverage = this.getLeverage();
540
- // remaining leverage
541
- // let remainingLeverage = userMaxLeverageSetting;
542
552
  const remainingLeverage = _1.BN.max(userMaxLeverageSetting.sub(currentLeverage), numericConstants_1.ZERO);
543
553
  // get total collateral
544
554
  const totalCollateral = this.getTotalCollateral();
@@ -546,9 +556,43 @@ class ClearingHouseUser {
546
556
  let maxPositionSize = remainingLeverage
547
557
  .mul(totalCollateral)
548
558
  .div(numericConstants_1.TEN_THOUSAND);
549
- // 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.
550
- const oppositeSizeValueUSDC = getOppositePositionValueUSDC();
551
- maxPositionSize = maxPositionSize.add(oppositeSizeValueUSDC.mul(new _1.BN(2)));
559
+ if (userMaxLeverageSetting.sub(currentLeverage).gte(numericConstants_1.ZERO)) {
560
+ if (oppositeSizeValueUSDC.eq(numericConstants_1.ZERO)) {
561
+ // case 1 : Regular trade where current total position less than max, and no opposite position to account for
562
+ // do nothing
563
+ }
564
+ else {
565
+ // case 2 : trade where current total position less than max, but need to account for flipping the current position over to the other side
566
+ maxPositionSize = maxPositionSize.add(oppositeSizeValueUSDC.mul(new _1.BN(2)));
567
+ }
568
+ }
569
+ else {
570
+ // current leverage is greater than max leverage - can only reduce position size
571
+ if (!targettingSameSide) {
572
+ const currentPositionQuoteSize = this.getPositionValue(targetMarketIndex);
573
+ const currentTotalQuoteSize = currentLeverage
574
+ .mul(totalCollateral)
575
+ .div(numericConstants_1.TEN_THOUSAND);
576
+ const otherPositionsTotalQuoteSize = currentTotalQuoteSize.sub(currentPositionQuoteSize);
577
+ const quoteValueOfMaxLeverage = userMaxLeverageSetting
578
+ .mul(totalCollateral)
579
+ .div(numericConstants_1.TEN_THOUSAND);
580
+ if (otherPositionsTotalQuoteSize
581
+ .sub(currentPositionQuoteSize)
582
+ .gte(quoteValueOfMaxLeverage)) {
583
+ // case 3: Can only reduce the current position because it will still be greater than max leverage
584
+ maxPositionSize = currentPositionQuoteSize;
585
+ }
586
+ else {
587
+ // case 4: Can reduce the position, and then take extra remaining quote to get to max leverage
588
+ const allowedQuoteSizeAfterClosingCurrentPosition = quoteValueOfMaxLeverage.sub(otherPositionsTotalQuoteSize);
589
+ maxPositionSize = currentPositionQuoteSize.add(allowedQuoteSizeAfterClosingCurrentPosition);
590
+ }
591
+ }
592
+ else {
593
+ // do nothing if targetting same side
594
+ }
595
+ }
552
596
  // subtract oneMillionth of maxPositionSize
553
597
  // => to avoid rounding errors when taking max leverage
554
598
  const oneMilli = maxPositionSize.div(numericConstants_1.QUOTE_PRECISION);
@@ -577,12 +621,18 @@ class ClearingHouseUser {
577
621
  .add(tradeQuoteAmount)
578
622
  .abs();
579
623
  const totalPositionAfterTradeExcludingTargetMarket = this.getTotalPositionValueExcludingMarket(targetMarketIndex);
580
- const newLeverage = currentMarketPositionAfterTrade
581
- .add(totalPositionAfterTradeExcludingTargetMarket)
582
- .abs()
583
- .mul(numericConstants_1.TEN_THOUSAND)
584
- .div(this.getTotalCollateral());
585
- return newLeverage;
624
+ const totalCollateral = this.getTotalCollateral();
625
+ if (totalCollateral.gt(numericConstants_1.ZERO)) {
626
+ const newLeverage = currentMarketPositionAfterTrade
627
+ .add(totalPositionAfterTradeExcludingTargetMarket)
628
+ .abs()
629
+ .mul(numericConstants_1.TEN_THOUSAND)
630
+ .div(totalCollateral);
631
+ return newLeverage;
632
+ }
633
+ else {
634
+ return new _1.BN(0);
635
+ }
586
636
  }
587
637
  /**
588
638
  * Calculates how much fee will be taken for a given sized trade
@@ -605,8 +655,7 @@ class ClearingHouseUser {
605
655
  this.getEmptyPosition(marketToIgnore);
606
656
  let currentMarketPositionValueUSDC = numericConstants_1.ZERO;
607
657
  if (currentMarketPosition) {
608
- const market = this.clearingHouse.getMarket(currentMarketPosition.marketIndex);
609
- currentMarketPositionValueUSDC = _1.calculateBaseAssetValue(market, currentMarketPosition);
658
+ currentMarketPositionValueUSDC = this.getPositionValue(marketToIgnore);
610
659
  }
611
660
  return this.getTotalPositionValue().sub(currentMarketPositionValueUSDC);
612
661
  }
package/lib/math/amm.d.ts CHANGED
@@ -64,3 +64,5 @@ export declare function calculateRepegCost(market: Market, marketIndex: BN, newP
64
64
  */
65
65
  export declare function calculateTerminalPrice(market: Market): BN;
66
66
  export declare function calculateMaxBaseAssetAmountToTrade(amm: AMM, limit_price: BN): [BN, PositionDirection];
67
+ export declare function calculateBudgetedK(market: Market, cost: BN): [BN, BN];
68
+ export declare function calculateBudgetedPeg(market: Market, cost: BN): BN;
package/lib/math/amm.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.calculateMaxBaseAssetAmountToTrade = exports.calculateTerminalPrice = exports.calculateRepegCost = exports.calculateAdjustKCost = exports.getSwapDirection = exports.calculateSwapOutput = exports.calculateAmmReservesAfterSwap = exports.calculatePrice = void 0;
3
+ exports.calculateBudgetedPeg = exports.calculateBudgetedK = exports.calculateMaxBaseAssetAmountToTrade = exports.calculateTerminalPrice = exports.calculateRepegCost = exports.calculateAdjustKCost = exports.getSwapDirection = exports.calculateSwapOutput = exports.calculateAmmReservesAfterSwap = exports.calculatePrice = void 0;
4
4
  const anchor_1 = require("@project-serum/anchor");
5
5
  const numericConstants_1 = require("../constants/numericConstants");
6
6
  const position_1 = require("./position");
@@ -116,6 +116,26 @@ function calculateAdjustKCost(market, marketIndex, numerator, denomenator) {
116
116
  marketNewK.amm.sqrtK = market.amm.sqrtK.mul(numerator).div(denomenator);
117
117
  netUserPosition.quoteAssetAmount = currentValue;
118
118
  const cost = __1.calculatePositionPNL(marketNewK, netUserPosition);
119
+ const p = numericConstants_1.PEG_PRECISION.mul(numerator).div(denomenator);
120
+ const x = market.amm.baseAssetReserve;
121
+ const y = market.amm.quoteAssetReserve;
122
+ const delta = market.baseAssetAmount;
123
+ const k = market.amm.sqrtK.mul(market.amm.sqrtK);
124
+ const numer1 = numericConstants_1.PEG_PRECISION.sub(p).mul(y).div(numericConstants_1.PEG_PRECISION);
125
+ const numer20 = k
126
+ .mul(p)
127
+ .mul(p)
128
+ .div(numericConstants_1.PEG_PRECISION)
129
+ .div(numericConstants_1.PEG_PRECISION)
130
+ .div(x.mul(p).div(numericConstants_1.PEG_PRECISION).add(delta));
131
+ const numer21 = k.div(x.add(delta));
132
+ const formulaCost = numer21
133
+ .sub(numer20)
134
+ .sub(numer1)
135
+ .mul(market.amm.pegMultiplier)
136
+ .div(numericConstants_1.AMM_TIMES_PEG_TO_QUOTE_PRECISION_RATIO);
137
+ console.log(__1.convertToNumber(formulaCost, numericConstants_1.QUOTE_PRECISION));
138
+ // p.div(p.mul(x).add(delta)).sub()
119
139
  return cost;
120
140
  }
121
141
  exports.calculateAdjustKCost = calculateAdjustKCost;
@@ -144,6 +164,13 @@ function calculateRepegCost(market, marketIndex, newPeg) {
144
164
  marketNewPeg.amm.pegMultiplier = newPeg;
145
165
  console.log('Price moves from', __1.convertToNumber(prevMarketPrice), 'to', __1.convertToNumber(__1.calculateMarkPrice(marketNewPeg)));
146
166
  const cost = __1.calculatePositionPNL(marketNewPeg, netUserPosition);
167
+ const k = market.amm.sqrtK.mul(market.amm.sqrtK);
168
+ const newQuoteAssetReserve = k.div(market.amm.baseAssetReserve.add(netUserPosition.baseAssetAmount));
169
+ const deltaQuoteAssetReserves = newQuoteAssetReserve.sub(market.amm.quoteAssetReserve);
170
+ const cost2 = deltaQuoteAssetReserves
171
+ .mul(market.amm.pegMultiplier.sub(newPeg))
172
+ .div(numericConstants_1.AMM_TIMES_PEG_TO_QUOTE_PRECISION_RATIO);
173
+ console.log(__1.convertToNumber(cost2, numericConstants_1.QUOTE_PRECISION));
147
174
  return cost;
148
175
  }
149
176
  exports.calculateRepegCost = calculateRepegCost;
@@ -192,3 +219,66 @@ function calculateMaxBaseAssetAmountToTrade(amm, limit_price) {
192
219
  }
193
220
  }
194
221
  exports.calculateMaxBaseAssetAmountToTrade = calculateMaxBaseAssetAmountToTrade;
222
+ function calculateBudgetedK(market, cost) {
223
+ // wolframalpha.com
224
+ // (1/(x+d) - p/(x*p+d))*y*d*Q = C solve for p
225
+ // p = (d(y*d*Q - C(x+d))) / (C*x(x+d) + y*y*d*Q)
226
+ // todo: assumes k = x * y
227
+ // otherwise use: (y(1-p) + (kp^2/(x*p+d)) - k/(x+d)) * Q = C solve for p
228
+ // const k = market.amm.sqrtK.mul(market.amm.sqrtK);
229
+ const x = market.amm.baseAssetReserve;
230
+ const y = market.amm.quoteAssetReserve;
231
+ const d = market.baseAssetAmount;
232
+ const Q = market.amm.pegMultiplier;
233
+ const C = cost.mul(new anchor_1.BN(-1));
234
+ const numer1 = y.mul(d).mul(Q).div(numericConstants_1.AMM_RESERVE_PRECISION).div(numericConstants_1.PEG_PRECISION);
235
+ const numer2 = C.mul(x.add(d)).div(numericConstants_1.QUOTE_PRECISION);
236
+ const denom1 = C.mul(x)
237
+ .mul(x.add(d))
238
+ .div(numericConstants_1.AMM_RESERVE_PRECISION)
239
+ .div(numericConstants_1.QUOTE_PRECISION);
240
+ const denom2 = y
241
+ .mul(d)
242
+ .mul(d)
243
+ .mul(Q)
244
+ .div(numericConstants_1.AMM_RESERVE_PRECISION)
245
+ .div(numericConstants_1.AMM_RESERVE_PRECISION)
246
+ .div(numericConstants_1.PEG_PRECISION);
247
+ const numerator = d
248
+ .mul(numer1.add(numer2))
249
+ .div(numericConstants_1.AMM_RESERVE_PRECISION)
250
+ .div(numericConstants_1.AMM_RESERVE_PRECISION)
251
+ .div(numericConstants_1.AMM_TO_QUOTE_PRECISION_RATIO);
252
+ const denominator = denom1
253
+ .add(denom2)
254
+ .div(numericConstants_1.AMM_RESERVE_PRECISION)
255
+ .div(numericConstants_1.AMM_TO_QUOTE_PRECISION_RATIO);
256
+ console.log(numerator, denominator);
257
+ // const p = (numerator).div(denominator);
258
+ // const formulaCost = (numer21.sub(numer20).sub(numer1)).mul(market.amm.pegMultiplier).div(AMM_TIMES_PEG_TO_QUOTE_PRECISION_RATIO)
259
+ // console.log(convertToNumber(formulaCost, QUOTE_PRECISION))
260
+ return [numerator, denominator];
261
+ }
262
+ exports.calculateBudgetedK = calculateBudgetedK;
263
+ function calculateBudgetedPeg(market, cost) {
264
+ // wolframalpha.com
265
+ // (1/(x+d) - p/(x*p+d))*y*d*Q = C solve for p
266
+ // p = (d(y*d*Q - C(x+d))) / (C*x(x+d) + y*y*d*Q)
267
+ // todo: assumes k = x * y
268
+ // otherwise use: (y(1-p) + (kp^2/(x*p+d)) - k/(x+d)) * Q = C solve for p
269
+ const k = market.amm.sqrtK.mul(market.amm.sqrtK);
270
+ const x = market.amm.baseAssetReserve;
271
+ const y = market.amm.quoteAssetReserve;
272
+ const d = market.baseAssetAmount;
273
+ const Q = market.amm.pegMultiplier;
274
+ const C = cost.mul(new anchor_1.BN(-1));
275
+ const deltaQuoteAssetReserves = y.sub(k.div(x.add(d)));
276
+ const deltaPegMultiplier = C.mul(numericConstants_1.MARK_PRICE_PRECISION)
277
+ .div(deltaQuoteAssetReserves.div(numericConstants_1.AMM_TO_QUOTE_PRECISION_RATIO))
278
+ .mul(numericConstants_1.PEG_PRECISION)
279
+ .div(numericConstants_1.QUOTE_PRECISION);
280
+ console.log(Q.toNumber(), 'change by', deltaPegMultiplier.toNumber() / numericConstants_1.MARK_PRICE_PRECISION.toNumber());
281
+ const newPeg = Q.sub(deltaPegMultiplier.mul(numericConstants_1.PEG_PRECISION).div(numericConstants_1.MARK_PRICE_PRECISION));
282
+ return newPeg;
283
+ }
284
+ exports.calculateBudgetedPeg = calculateBudgetedPeg;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@drift-labs/sdk",
3
- "version": "0.1.21-master.3",
3
+ "version": "0.1.21-master.4",
4
4
  "main": "lib/index.js",
5
5
  "types": "lib/index.d.ts",
6
6
  "author": "crispheaney",
@@ -733,7 +733,22 @@ export class ClearingHouseUser {
733
733
 
734
734
  /**
735
735
  * Get the maximum trade size for a given market, taking into account the user's current leverage, positions, collateral, etc.
736
- * @param marketIndex
736
+ *
737
+ * To Calculate Max Quote Available:
738
+ *
739
+ * Case 1: SameSide
740
+ * => Remaining quote to get to maxLeverage
741
+ *
742
+ * Case 2: NOT SameSide && currentLeverage <= maxLeverage
743
+ * => Current opposite position x2 + remaining to get to maxLeverage
744
+ *
745
+ * Case 3: NOT SameSide && currentLeverage > maxLeverage && otherPositions - currentPosition > maxLeverage
746
+ * => strictly reduce current position size
747
+ *
748
+ * Case 4: NOT SameSide && currentLeverage > maxLeverage && otherPositions - currentPosition < maxLeverage
749
+ * => current position + remaining to get to maxLeverage
750
+ *
751
+ * @param targetMarketIndex
737
752
  * @param tradeSide
738
753
  * @param userMaxLeverageSetting - leverage : Precision TEN_THOUSAND
739
754
  * @returns tradeSizeAllowed : Precision QUOTE_PRECISION
@@ -743,34 +758,28 @@ export class ClearingHouseUser {
743
758
  tradeSide: PositionDirection,
744
759
  userMaxLeverageSetting: BN
745
760
  ): BN {
746
- // inline function which get's the current position size on the opposite side of the target trade
747
- const getOppositePositionValueUSDC = () => {
748
- if (!currentPosition) return ZERO;
761
+ const currentPosition =
762
+ this.getUserPosition(targetMarketIndex) ||
763
+ this.getEmptyPosition(targetMarketIndex);
749
764
 
750
- const side = tradeSide === PositionDirection.SHORT ? 'short' : 'long';
765
+ const targetSide = tradeSide === PositionDirection.SHORT ? 'short' : 'long';
751
766
 
752
- if (side === 'long' && currentPosition?.baseAssetAmount.isNeg()) {
753
- return this.getPositionValue(targetMarketIndex);
754
- } else if (
755
- side === 'short' &&
756
- !currentPosition?.baseAssetAmount.isNeg()
757
- ) {
758
- return this.getPositionValue(targetMarketIndex);
759
- }
767
+ const currentPositionSide = currentPosition?.baseAssetAmount.isNeg()
768
+ ? 'short'
769
+ : 'long';
760
770
 
761
- return ZERO;
762
- };
771
+ const targettingSameSide = !currentPosition
772
+ ? true
773
+ : targetSide === currentPositionSide;
763
774
 
764
- const currentPosition =
765
- this.getUserPosition(targetMarketIndex) ||
766
- this.getEmptyPosition(targetMarketIndex);
775
+ // 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
777
+ ? ZERO
778
+ : this.getPositionValue(targetMarketIndex);
767
779
 
768
780
  // get current leverage
769
781
  const currentLeverage = this.getLeverage();
770
782
 
771
- // remaining leverage
772
- // let remainingLeverage = userMaxLeverageSetting;
773
-
774
783
  const remainingLeverage = BN.max(
775
784
  userMaxLeverageSetting.sub(currentLeverage),
776
785
  ZERO
@@ -784,10 +793,57 @@ export class ClearingHouseUser {
784
793
  .mul(totalCollateral)
785
794
  .div(TEN_THOUSAND);
786
795
 
787
- // 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.
788
- const oppositeSizeValueUSDC = getOppositePositionValueUSDC();
796
+ if (userMaxLeverageSetting.sub(currentLeverage).gte(ZERO)) {
797
+ if (oppositeSizeValueUSDC.eq(ZERO)) {
798
+ // case 1 : Regular trade where current total position less than max, and no opposite position to account for
799
+ // do nothing
800
+ } else {
801
+ // case 2 : trade where current total position less than max, but need to account for flipping the current position over to the other side
802
+ maxPositionSize = maxPositionSize.add(
803
+ oppositeSizeValueUSDC.mul(new BN(2))
804
+ );
805
+ }
806
+ } else {
807
+ // current leverage is greater than max leverage - can only reduce position size
789
808
 
790
- maxPositionSize = maxPositionSize.add(oppositeSizeValueUSDC.mul(new BN(2)));
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;
833
+ } 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
841
+ );
842
+ }
843
+ } else {
844
+ // do nothing if targetting same side
845
+ }
846
+ }
791
847
 
792
848
  // subtract oneMillionth of maxPositionSize
793
849
  // => to avoid rounding errors when taking max leverage
@@ -832,13 +888,18 @@ export class ClearingHouseUser {
832
888
 
833
889
  const totalPositionAfterTradeExcludingTargetMarket =
834
890
  this.getTotalPositionValueExcludingMarket(targetMarketIndex);
835
- const newLeverage = currentMarketPositionAfterTrade
836
- .add(totalPositionAfterTradeExcludingTargetMarket)
837
- .abs()
838
- .mul(TEN_THOUSAND)
839
- .div(this.getTotalCollateral());
840
891
 
841
- return newLeverage;
892
+ const totalCollateral = this.getTotalCollateral();
893
+ if (totalCollateral.gt(ZERO)) {
894
+ const newLeverage = currentMarketPositionAfterTrade
895
+ .add(totalPositionAfterTradeExcludingTargetMarket)
896
+ .abs()
897
+ .mul(TEN_THOUSAND)
898
+ .div(totalCollateral);
899
+ return newLeverage;
900
+ } else {
901
+ return new BN(0);
902
+ }
842
903
  }
843
904
 
844
905
  /**
@@ -866,13 +927,7 @@ export class ClearingHouseUser {
866
927
 
867
928
  let currentMarketPositionValueUSDC = ZERO;
868
929
  if (currentMarketPosition) {
869
- const market = this.clearingHouse.getMarket(
870
- currentMarketPosition.marketIndex
871
- );
872
- currentMarketPositionValueUSDC = calculateBaseAssetValue(
873
- market,
874
- currentMarketPosition
875
- );
930
+ currentMarketPositionValueUSDC = this.getPositionValue(marketToIgnore);
876
931
  }
877
932
 
878
933
  return this.getTotalPositionValue().sub(currentMarketPositionValueUSDC);
package/src/math/amm.ts CHANGED
@@ -4,6 +4,9 @@ import {
4
4
  MARK_PRICE_PRECISION,
5
5
  PEG_PRECISION,
6
6
  ZERO,
7
+ AMM_TO_QUOTE_PRECISION_RATIO,
8
+ QUOTE_PRECISION,
9
+ AMM_RESERVE_PRECISION,
7
10
  } from '../constants/numericConstants';
8
11
  import { calculateBaseAssetValue } from './position';
9
12
  import {
@@ -175,6 +178,30 @@ export function calculateAdjustKCost(
175
178
 
176
179
  const cost = calculatePositionPNL(marketNewK, netUserPosition);
177
180
 
181
+ const p = PEG_PRECISION.mul(numerator).div(denomenator);
182
+ const x = market.amm.baseAssetReserve;
183
+ const y = market.amm.quoteAssetReserve;
184
+ const delta = market.baseAssetAmount;
185
+ const k = market.amm.sqrtK.mul(market.amm.sqrtK);
186
+
187
+ const numer1 = PEG_PRECISION.sub(p).mul(y).div(PEG_PRECISION);
188
+ const numer20 = k
189
+ .mul(p)
190
+ .mul(p)
191
+ .div(PEG_PRECISION)
192
+ .div(PEG_PRECISION)
193
+ .div(x.mul(p).div(PEG_PRECISION).add(delta));
194
+ const numer21 = k.div(x.add(delta));
195
+
196
+ const formulaCost = numer21
197
+ .sub(numer20)
198
+ .sub(numer1)
199
+ .mul(market.amm.pegMultiplier)
200
+ .div(AMM_TIMES_PEG_TO_QUOTE_PRECISION_RATIO);
201
+ console.log(convertToNumber(formulaCost, QUOTE_PRECISION));
202
+
203
+ // p.div(p.mul(x).add(delta)).sub()
204
+
178
205
  return cost;
179
206
  }
180
207
 
@@ -217,6 +244,17 @@ export function calculateRepegCost(
217
244
 
218
245
  const cost = calculatePositionPNL(marketNewPeg, netUserPosition);
219
246
 
247
+ const k = market.amm.sqrtK.mul(market.amm.sqrtK);
248
+ const newQuoteAssetReserve = k.div(
249
+ market.amm.baseAssetReserve.add(netUserPosition.baseAssetAmount)
250
+ );
251
+ const deltaQuoteAssetReserves = newQuoteAssetReserve.sub(
252
+ market.amm.quoteAssetReserve
253
+ );
254
+ const cost2 = deltaQuoteAssetReserves
255
+ .mul(market.amm.pegMultiplier.sub(newPeg))
256
+ .div(AMM_TIMES_PEG_TO_QUOTE_PRECISION_RATIO);
257
+ console.log(convertToNumber(cost2, QUOTE_PRECISION));
220
258
  return cost;
221
259
  }
222
260
 
@@ -238,6 +276,7 @@ export function calculateTerminalPrice(market: Market) {
238
276
  market.baseAssetAmount.abs(),
239
277
  getSwapDirection('base', directionToClose)
240
278
  );
279
+
241
280
  const terminalPrice = newQuoteAssetReserve
242
281
  .mul(MARK_PRICE_PRECISION)
243
282
  .mul(market.amm.pegMultiplier)
@@ -276,3 +315,86 @@ export function calculateMaxBaseAssetAmountToTrade(
276
315
  return [new BN(0), PositionDirection.LONG];
277
316
  }
278
317
  }
318
+
319
+ export function calculateBudgetedK(market: Market, cost: BN): [BN, BN] {
320
+ // wolframalpha.com
321
+ // (1/(x+d) - p/(x*p+d))*y*d*Q = C solve for p
322
+ // p = (d(y*d*Q - C(x+d))) / (C*x(x+d) + y*y*d*Q)
323
+
324
+ // todo: assumes k = x * y
325
+ // otherwise use: (y(1-p) + (kp^2/(x*p+d)) - k/(x+d)) * Q = C solve for p
326
+
327
+ // const k = market.amm.sqrtK.mul(market.amm.sqrtK);
328
+ const x = market.amm.baseAssetReserve;
329
+ const y = market.amm.quoteAssetReserve;
330
+
331
+ const d = market.baseAssetAmount;
332
+ const Q = market.amm.pegMultiplier;
333
+
334
+ const C = cost.mul(new BN(-1));
335
+
336
+ const numer1 = y.mul(d).mul(Q).div(AMM_RESERVE_PRECISION).div(PEG_PRECISION);
337
+ const numer2 = C.mul(x.add(d)).div(QUOTE_PRECISION);
338
+ const denom1 = C.mul(x)
339
+ .mul(x.add(d))
340
+ .div(AMM_RESERVE_PRECISION)
341
+ .div(QUOTE_PRECISION);
342
+ const denom2 = y
343
+ .mul(d)
344
+ .mul(d)
345
+ .mul(Q)
346
+ .div(AMM_RESERVE_PRECISION)
347
+ .div(AMM_RESERVE_PRECISION)
348
+ .div(PEG_PRECISION);
349
+
350
+ const numerator = d
351
+ .mul(numer1.add(numer2))
352
+ .div(AMM_RESERVE_PRECISION)
353
+ .div(AMM_RESERVE_PRECISION)
354
+ .div(AMM_TO_QUOTE_PRECISION_RATIO);
355
+ const denominator = denom1
356
+ .add(denom2)
357
+ .div(AMM_RESERVE_PRECISION)
358
+ .div(AMM_TO_QUOTE_PRECISION_RATIO);
359
+ console.log(numerator, denominator);
360
+ // const p = (numerator).div(denominator);
361
+
362
+ // const formulaCost = (numer21.sub(numer20).sub(numer1)).mul(market.amm.pegMultiplier).div(AMM_TIMES_PEG_TO_QUOTE_PRECISION_RATIO)
363
+ // console.log(convertToNumber(formulaCost, QUOTE_PRECISION))
364
+
365
+ return [numerator, denominator];
366
+ }
367
+
368
+ export function calculateBudgetedPeg(market: Market, cost: BN): BN {
369
+ // wolframalpha.com
370
+ // (1/(x+d) - p/(x*p+d))*y*d*Q = C solve for p
371
+ // p = (d(y*d*Q - C(x+d))) / (C*x(x+d) + y*y*d*Q)
372
+
373
+ // todo: assumes k = x * y
374
+ // otherwise use: (y(1-p) + (kp^2/(x*p+d)) - k/(x+d)) * Q = C solve for p
375
+
376
+ const k = market.amm.sqrtK.mul(market.amm.sqrtK);
377
+ const x = market.amm.baseAssetReserve;
378
+ const y = market.amm.quoteAssetReserve;
379
+
380
+ const d = market.baseAssetAmount;
381
+ const Q = market.amm.pegMultiplier;
382
+
383
+ const C = cost.mul(new BN(-1));
384
+
385
+ const deltaQuoteAssetReserves = y.sub(k.div(x.add(d)));
386
+ const deltaPegMultiplier = C.mul(MARK_PRICE_PRECISION)
387
+ .div(deltaQuoteAssetReserves.div(AMM_TO_QUOTE_PRECISION_RATIO))
388
+ .mul(PEG_PRECISION)
389
+ .div(QUOTE_PRECISION);
390
+ console.log(
391
+ Q.toNumber(),
392
+ 'change by',
393
+ deltaPegMultiplier.toNumber() / MARK_PRICE_PRECISION.toNumber()
394
+ );
395
+ const newPeg = Q.sub(
396
+ deltaPegMultiplier.mul(PEG_PRECISION).div(MARK_PRICE_PRECISION)
397
+ );
398
+
399
+ return newPeg;
400
+ }