@drift-labs/sdk 0.2.0-master.11 → 0.2.0-master.14

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 (114) hide show
  1. package/lib/accounts/fetch.d.ts +2 -1
  2. package/lib/accounts/fetch.js +9 -1
  3. package/lib/accounts/pollingUserStatsAccountSubscriber.d.ts +27 -0
  4. package/lib/accounts/pollingUserStatsAccountSubscriber.js +113 -0
  5. package/lib/accounts/types.d.ts +14 -1
  6. package/lib/accounts/webSocketUserStatsAccountSubsriber.d.ts +20 -0
  7. package/lib/accounts/webSocketUserStatsAccountSubsriber.js +47 -0
  8. package/lib/addresses/pda.d.ts +1 -0
  9. package/lib/addresses/pda.js +8 -1
  10. package/lib/admin.d.ts +2 -0
  11. package/lib/admin.js +18 -0
  12. package/lib/clearingHouse.d.ts +24 -3
  13. package/lib/clearingHouse.js +352 -51
  14. package/lib/clearingHouseConfig.d.ts +1 -0
  15. package/lib/clearingHouseUser.d.ts +15 -15
  16. package/lib/clearingHouseUser.js +185 -73
  17. package/lib/clearingHouseUserStats.d.ts +17 -0
  18. package/lib/clearingHouseUserStats.js +36 -0
  19. package/lib/clearingHouseUserStatsConfig.d.ts +14 -0
  20. package/lib/clearingHouseUserStatsConfig.js +2 -0
  21. package/lib/config.js +1 -1
  22. package/lib/constants/banks.d.ts +2 -2
  23. package/lib/constants/banks.js +4 -3
  24. package/lib/constants/numericConstants.d.ts +2 -0
  25. package/lib/constants/numericConstants.js +3 -1
  26. package/lib/events/eventList.js +3 -0
  27. package/lib/events/types.d.ts +2 -1
  28. package/lib/factory/bigNum.d.ts +1 -0
  29. package/lib/factory/bigNum.js +37 -11
  30. package/lib/idl/clearing_house.json +692 -58
  31. package/lib/index.d.ts +1 -0
  32. package/lib/index.js +1 -0
  33. package/lib/math/amm.js +2 -2
  34. package/lib/math/bankBalance.d.ts +7 -1
  35. package/lib/math/bankBalance.js +76 -1
  36. package/lib/math/margin.d.ts +11 -0
  37. package/lib/math/margin.js +72 -0
  38. package/lib/math/market.d.ts +4 -1
  39. package/lib/math/market.js +35 -1
  40. package/lib/math/oracles.d.ts +3 -0
  41. package/lib/math/oracles.js +25 -5
  42. package/lib/math/position.d.ts +8 -0
  43. package/lib/math/position.js +43 -12
  44. package/lib/math/trade.js +2 -2
  45. package/lib/orders.d.ts +1 -2
  46. package/lib/orders.js +2 -77
  47. package/lib/tokenFaucet.d.ts +1 -0
  48. package/lib/tokenFaucet.js +23 -12
  49. package/lib/tx/retryTxSender.js +9 -2
  50. package/lib/types.d.ts +78 -10
  51. package/lib/types.js +12 -0
  52. package/lib/util/getTokenAddress.d.ts +2 -0
  53. package/lib/util/getTokenAddress.js +9 -0
  54. package/package.json +1 -1
  55. package/src/accounts/fetch.ts +27 -2
  56. package/src/accounts/pollingUserStatsAccountSubscriber.ts +172 -0
  57. package/src/accounts/types.ts +18 -0
  58. package/src/accounts/webSocketUserStatsAccountSubsriber.ts +80 -0
  59. package/src/addresses/pda.ts +13 -0
  60. package/src/admin.ts +29 -1
  61. package/src/clearingHouse.ts +619 -62
  62. package/src/clearingHouseConfig.ts +1 -0
  63. package/src/clearingHouseUser.ts +317 -105
  64. package/src/clearingHouseUserStats.ts +53 -0
  65. package/src/clearingHouseUserStatsConfig.ts +18 -0
  66. package/src/config.ts +1 -1
  67. package/src/constants/banks.js +42 -0
  68. package/src/constants/banks.ts +6 -3
  69. package/src/constants/markets.js +42 -0
  70. package/src/constants/numericConstants.js +41 -0
  71. package/src/constants/numericConstants.ts +3 -0
  72. package/src/events/eventList.ts +3 -0
  73. package/src/events/types.ts +2 -0
  74. package/src/factory/bigNum.js +37 -11
  75. package/src/factory/bigNum.ts +43 -13
  76. package/src/idl/clearing_house.json +692 -58
  77. package/src/index.ts +1 -0
  78. package/src/math/amm.ts +8 -5
  79. package/src/math/bankBalance.ts +147 -1
  80. package/src/math/margin.ts +124 -0
  81. package/src/math/market.ts +66 -1
  82. package/src/math/oracles.ts +42 -5
  83. package/src/math/position.ts +60 -9
  84. package/src/math/trade.ts +2 -2
  85. package/src/orders.ts +4 -157
  86. package/src/tokenFaucet.js +189 -0
  87. package/src/tokenFaucet.ts +38 -15
  88. package/src/tx/retryTxSender.ts +11 -3
  89. package/src/types.js +12 -1
  90. package/src/types.ts +83 -10
  91. package/src/{accounts/fetch.js → util/computeUnits.js} +11 -13
  92. package/src/util/getTokenAddress.js +9 -0
  93. package/src/util/getTokenAddress.ts +18 -0
  94. package/tests/bn/test.ts +2 -0
  95. package/src/accounts/bulkAccountLoader.js +0 -197
  96. package/src/accounts/bulkUserSubscription.js +0 -33
  97. package/src/accounts/pollingClearingHouseAccountSubscriber.js +0 -311
  98. package/src/accounts/pollingOracleSubscriber.js +0 -93
  99. package/src/accounts/pollingTokenAccountSubscriber.js +0 -90
  100. package/src/accounts/pollingUserAccountSubscriber.js +0 -132
  101. package/src/accounts/types.js +0 -10
  102. package/src/accounts/utils.js +0 -7
  103. package/src/accounts/webSocketAccountSubscriber.js +0 -93
  104. package/src/accounts/webSocketClearingHouseAccountSubscriber.js +0 -233
  105. package/src/accounts/webSocketUserAccountSubscriber.js +0 -62
  106. package/src/addresses/pda.js +0 -104
  107. package/src/index.js +0 -100
  108. package/src/math/bankBalance.js +0 -75
  109. package/src/math/market.js +0 -57
  110. package/src/math/orders.js +0 -110
  111. package/src/math/position.js +0 -140
  112. package/src/mockUSDCFaucet.js +0 -280
  113. package/src/orders.js +0 -134
  114. package/src/tx/retryTxSender.js +0 -188
package/src/index.ts CHANGED
@@ -35,6 +35,7 @@ export * from './math/amm';
35
35
  export * from './math/trade';
36
36
  export * from './math/orders';
37
37
  export * from './math/repeg';
38
+ export * from './math/margin';
38
39
  export * from './orders';
39
40
  export * from './orderParams';
40
41
  export * from './slot/SlotSubscriber';
package/src/math/amm.ts CHANGED
@@ -33,11 +33,14 @@ export function calculatePegFromTargetPrice(
33
33
  baseAssetReserve: BN,
34
34
  quoteAssetReserve: BN
35
35
  ): BN {
36
- return targetPrice
37
- .mul(baseAssetReserve)
38
- .div(quoteAssetReserve)
39
- .add(PRICE_DIV_PEG.div(new BN(2)))
40
- .div(PRICE_DIV_PEG);
36
+ return BN.max(
37
+ targetPrice
38
+ .mul(baseAssetReserve)
39
+ .div(quoteAssetReserve)
40
+ .add(PRICE_DIV_PEG.div(new BN(2)))
41
+ .div(PRICE_DIV_PEG),
42
+ ONE
43
+ );
41
44
  }
42
45
 
43
46
  export function calculateOptimalPegAndBudget(
@@ -1,4 +1,9 @@
1
- import { BankAccount, BankBalanceType, isVariant } from '../types';
1
+ import {
2
+ BankAccount,
3
+ BankBalanceType,
4
+ isVariant,
5
+ MarginCategory,
6
+ } from '../types';
2
7
  import { BN } from '@project-serum/anchor';
3
8
  import {
4
9
  BANK_UTILIZATION_PRECISION,
@@ -6,8 +11,14 @@ import {
6
11
  TEN,
7
12
  ZERO,
8
13
  BANK_INTEREST_PRECISION,
14
+ BANK_WEIGHT_PRECISION,
9
15
  ONE_YEAR,
16
+ AMM_RESERVE_PRECISION,
10
17
  } from '../constants/numericConstants';
18
+ import {
19
+ calculateSizeDiscountAssetWeight,
20
+ calculateSizePremiumLiabilityWeight,
21
+ } from './margin';
11
22
 
12
23
  export function getBalance(
13
24
  tokenAmount: BN,
@@ -43,6 +54,92 @@ export function getTokenAmount(
43
54
  return balanceAmount.mul(cumulativeInterest).div(precisionDecrease);
44
55
  }
45
56
 
57
+ export function calculateAssetWeight(
58
+ balanceAmount: BN,
59
+ bank: BankAccount,
60
+ marginCategory: MarginCategory
61
+ ): BN {
62
+ const sizePrecision = TEN.pow(new BN(bank.decimals));
63
+ let sizeInAmmReservePrecision;
64
+ if (sizePrecision.gt(AMM_RESERVE_PRECISION)) {
65
+ sizeInAmmReservePrecision = balanceAmount.div(
66
+ sizePrecision.div(AMM_RESERVE_PRECISION)
67
+ );
68
+ } else {
69
+ sizeInAmmReservePrecision = balanceAmount
70
+ .mul(AMM_RESERVE_PRECISION)
71
+ .div(sizePrecision);
72
+ }
73
+
74
+ let assetWeight;
75
+
76
+ switch (marginCategory) {
77
+ case 'Initial':
78
+ assetWeight = calculateSizeDiscountAssetWeight(
79
+ sizeInAmmReservePrecision,
80
+ bank.imfFactor,
81
+ bank.initialAssetWeight
82
+ );
83
+ break;
84
+ case 'Maintenance':
85
+ assetWeight = calculateSizeDiscountAssetWeight(
86
+ sizeInAmmReservePrecision,
87
+ bank.imfFactor,
88
+ bank.maintenanceAssetWeight
89
+ );
90
+ break;
91
+ default:
92
+ assetWeight = bank.initialAssetWeight;
93
+ break;
94
+ }
95
+
96
+ return assetWeight;
97
+ }
98
+
99
+ export function calculateLiabilityWeight(
100
+ balanceAmount: BN,
101
+ bank: BankAccount,
102
+ marginCategory: MarginCategory
103
+ ): BN {
104
+ const sizePrecision = TEN.pow(new BN(bank.decimals));
105
+ let sizeInAmmReservePrecision;
106
+ if (sizePrecision.gt(AMM_RESERVE_PRECISION)) {
107
+ sizeInAmmReservePrecision = balanceAmount.div(
108
+ sizePrecision.div(AMM_RESERVE_PRECISION)
109
+ );
110
+ } else {
111
+ sizeInAmmReservePrecision = balanceAmount
112
+ .mul(AMM_RESERVE_PRECISION)
113
+ .div(sizePrecision);
114
+ }
115
+
116
+ let assetWeight;
117
+
118
+ switch (marginCategory) {
119
+ case 'Initial':
120
+ assetWeight = calculateSizePremiumLiabilityWeight(
121
+ sizeInAmmReservePrecision,
122
+ bank.imfFactor,
123
+ bank.initialLiabilityWeight,
124
+ BANK_WEIGHT_PRECISION
125
+ );
126
+ break;
127
+ case 'Maintenance':
128
+ assetWeight = calculateSizePremiumLiabilityWeight(
129
+ sizeInAmmReservePrecision,
130
+ bank.imfFactor,
131
+ bank.maintenanceLiabilityWeight,
132
+ BANK_WEIGHT_PRECISION
133
+ );
134
+ break;
135
+ default:
136
+ assetWeight = bank.initialLiabilityWeight;
137
+ break;
138
+ }
139
+
140
+ return assetWeight;
141
+ }
142
+
46
143
  export function calculateInterestAccumulated(
47
144
  bank: BankAccount,
48
145
  now: BN
@@ -110,3 +207,52 @@ export function calculateInterestAccumulated(
110
207
 
111
208
  return { borrowInterest, depositInterest };
112
209
  }
210
+
211
+ export function calculateWithdrawLimit(
212
+ bank: BankAccount,
213
+ now: BN
214
+ ): { borrowLimit: BN; withdrawLimit: BN } {
215
+ const bankDepositTokenAmount = getTokenAmount(
216
+ bank.depositBalance,
217
+ bank,
218
+ BankBalanceType.DEPOSIT
219
+ );
220
+ const bankBorrowTokenAmount = getTokenAmount(
221
+ bank.borrowBalance,
222
+ bank,
223
+ BankBalanceType.BORROW
224
+ );
225
+
226
+ const twentyFourHours = new BN(60 * 60 * 24);
227
+ const sinceLast = now.sub(bank.lastUpdated);
228
+ const sinceStart = BN.max(ZERO, twentyFourHours.sub(sinceLast));
229
+ const borrowTokenTwapLive = bank.borrowTokenTwap
230
+ .mul(sinceStart)
231
+ .add(bankBorrowTokenAmount.mul(sinceLast))
232
+ .div(sinceLast.add(sinceLast));
233
+
234
+ const depositTokenTwapLive = bank.depositTokenTwap
235
+ .mul(sinceStart)
236
+ .add(bankDepositTokenAmount.mul(sinceLast))
237
+ .div(sinceLast.add(sinceLast));
238
+
239
+ const maxBorrowTokens = BN.min(
240
+ BN.max(
241
+ bankDepositTokenAmount.div(new BN(6)),
242
+ borrowTokenTwapLive.add(borrowTokenTwapLive.div(new BN(5)))
243
+ ),
244
+ bankDepositTokenAmount.sub(bankDepositTokenAmount.div(new BN(10)))
245
+ ); // between ~15-90% utilization with friction on twap
246
+
247
+ const minDepositTokens = depositTokenTwapLive.sub(
248
+ BN.min(
249
+ BN.max(depositTokenTwapLive.div(new BN(5)), bank.withdrawGuardThreshold),
250
+ depositTokenTwapLive
251
+ )
252
+ );
253
+
254
+ return {
255
+ borrowLimit: maxBorrowTokens.sub(bankBorrowTokenAmount),
256
+ withdrawLimit: bankDepositTokenAmount.sub(minDepositTokens),
257
+ };
258
+ }
@@ -0,0 +1,124 @@
1
+ import { squareRootBN } from './utils';
2
+ import {
3
+ BANK_WEIGHT_PRECISION,
4
+ BANK_IMF_PRECISION,
5
+ ZERO,
6
+ BID_ASK_SPREAD_PRECISION,
7
+ AMM_TO_QUOTE_PRECISION_RATIO,
8
+ MARK_PRICE_PRECISION,
9
+ } from '../constants/numericConstants';
10
+ import { BN } from '@project-serum/anchor';
11
+ import { OraclePriceData } from '../oracles/types';
12
+ import { MarketAccount, UserPosition } from '..';
13
+
14
+ export function calculateSizePremiumLiabilityWeight(
15
+ size: BN, // AMM_RESERVE_PRECISION
16
+ imfFactor: BN,
17
+ liabilityWeight: BN,
18
+ precision: BN
19
+ ): BN {
20
+ if (imfFactor.eq(ZERO)) {
21
+ return liabilityWeight;
22
+ }
23
+
24
+ const sizeSqrt = squareRootBN(size.div(new BN(1000)).add(new BN(1))); //1e13 -> 1e10 -> 1e5
25
+ const liabilityWeightNumerator = liabilityWeight.sub(
26
+ liabilityWeight.div(BN.max(new BN(1), BANK_IMF_PRECISION.div(imfFactor)))
27
+ );
28
+
29
+ const sizePremiumLiabilityWeight = liabilityWeightNumerator.add(
30
+ sizeSqrt // 1e5
31
+ .mul(imfFactor)
32
+ .div(new BN(100_000).mul(BANK_IMF_PRECISION).div(precision)) // 1e5
33
+ );
34
+
35
+ const maxLiabilityWeight = BN.max(
36
+ liabilityWeight,
37
+ sizePremiumLiabilityWeight
38
+ );
39
+ return maxLiabilityWeight;
40
+ }
41
+
42
+ export function calculateSizeDiscountAssetWeight(
43
+ size: BN, // AMM_RESERVE_PRECISION
44
+ imfFactor: BN,
45
+ assetWeight: BN
46
+ ): BN {
47
+ if (imfFactor.eq(ZERO)) {
48
+ return assetWeight;
49
+ }
50
+
51
+ const sizeSqrt = squareRootBN(size.div(new BN(1000)).add(new BN(1))); //1e13 -> 1e10 -> 1e5
52
+ const imfNumerator = BANK_IMF_PRECISION.add(
53
+ BANK_IMF_PRECISION.div(new BN(10))
54
+ );
55
+
56
+ const sizeDiscountAssetWeight = imfNumerator.mul(BANK_WEIGHT_PRECISION).div(
57
+ BANK_IMF_PRECISION.add(
58
+ sizeSqrt // 1e5
59
+ .mul(imfFactor)
60
+ .div(new BN(100_000)) // 1e5
61
+ )
62
+ );
63
+
64
+ const minAssetWeight = BN.min(assetWeight, sizeDiscountAssetWeight);
65
+
66
+ return minAssetWeight;
67
+ }
68
+
69
+ export function calculateOraclePriceForPerpMargin(
70
+ marketPosition: UserPosition,
71
+ market: MarketAccount,
72
+ oraclePriceData: OraclePriceData
73
+ ): BN {
74
+ const oraclePriceOffset = BN.min(
75
+ new BN(market.amm.maxSpread)
76
+ .mul(oraclePriceData.price)
77
+ .div(BID_ASK_SPREAD_PRECISION),
78
+ oraclePriceData.confidence.add(
79
+ new BN(market.amm.baseSpread)
80
+ .mul(oraclePriceData.price)
81
+ .div(BID_ASK_SPREAD_PRECISION)
82
+ )
83
+ );
84
+
85
+ let marginPrice: BN;
86
+ if (marketPosition.baseAssetAmount.gt(ZERO)) {
87
+ marginPrice = oraclePriceData.price.sub(oraclePriceOffset);
88
+ } else {
89
+ marginPrice = oraclePriceData.price.add(oraclePriceOffset);
90
+ }
91
+
92
+ return marginPrice;
93
+ }
94
+
95
+ export function calculateMarginBaseAssetValue(
96
+ market: MarketAccount,
97
+ marketPosition: UserPosition,
98
+ oraclePriceData: OraclePriceData
99
+ ): BN {
100
+ const marginPrice = calculateOraclePriceForPerpMargin(
101
+ marketPosition,
102
+ market,
103
+ oraclePriceData
104
+ );
105
+ const baseAssetValue = marketPosition.baseAssetAmount
106
+ .abs()
107
+ .mul(marginPrice)
108
+ .div(AMM_TO_QUOTE_PRECISION_RATIO.mul(MARK_PRICE_PRECISION));
109
+
110
+ return baseAssetValue;
111
+ }
112
+
113
+ export function calculateWorstCaseBaseAssetAmount(
114
+ marketPosition: UserPosition
115
+ ): BN {
116
+ const allBids = marketPosition.baseAssetAmount.add(marketPosition.openBids);
117
+ const allAsks = marketPosition.baseAssetAmount.add(marketPosition.openAsks);
118
+
119
+ if (allBids.abs().gt(allAsks.abs())) {
120
+ return allBids;
121
+ } else {
122
+ return allAsks;
123
+ }
124
+ }
@@ -1,5 +1,11 @@
1
1
  import { BN } from '@project-serum/anchor';
2
- import { MarketAccount, PositionDirection } from '../types';
2
+ import {
3
+ MarketAccount,
4
+ PositionDirection,
5
+ MarginCategory,
6
+ BankAccount,
7
+ BankBalanceType,
8
+ } from '../types';
3
9
  import {
4
10
  calculateAmmReservesAfterSwap,
5
11
  calculatePrice,
@@ -7,7 +13,13 @@ import {
7
13
  getSwapDirection,
8
14
  calculateUpdatedAMM,
9
15
  } from './amm';
16
+ import {
17
+ calculateSizeDiscountAssetWeight,
18
+ calculateSizePremiumLiabilityWeight,
19
+ } from './margin';
10
20
  import { OraclePriceData } from '../oracles/types';
21
+ import { MARGIN_PRECISION } from '../constants/numericConstants';
22
+ import { getTokenAmount } from './bankBalance';
11
23
 
12
24
  /**
13
25
  * Calculates market mark price
@@ -103,3 +115,56 @@ export function calculateOracleSpread(
103
115
  ): BN {
104
116
  return price.sub(oraclePriceData.price);
105
117
  }
118
+
119
+ export function calculateMarketMarginRatio(
120
+ market: MarketAccount,
121
+ size: BN,
122
+ marginCategory: MarginCategory
123
+ ): number {
124
+ let marginRatio;
125
+ switch (marginCategory) {
126
+ case 'Initial':
127
+ marginRatio = calculateSizePremiumLiabilityWeight(
128
+ size,
129
+ market.imfFactor,
130
+ new BN(market.marginRatioInitial),
131
+ MARGIN_PRECISION
132
+ ).toNumber();
133
+ break;
134
+ case 'Maintenance':
135
+ marginRatio = market.marginRatioMaintenance;
136
+ break;
137
+ }
138
+
139
+ return marginRatio;
140
+ }
141
+
142
+ export function calculateUnsettledAssetWeight(
143
+ market: MarketAccount,
144
+ unsettledPnl: BN,
145
+ marginCategory: MarginCategory
146
+ ): BN {
147
+ let assetWeight: BN;
148
+
149
+ switch (marginCategory) {
150
+ case 'Initial':
151
+ assetWeight = calculateSizeDiscountAssetWeight(
152
+ unsettledPnl,
153
+ market.unsettledImfFactor,
154
+ new BN(market.unsettledInitialAssetWeight)
155
+ );
156
+ break;
157
+ case 'Maintenance':
158
+ assetWeight = new BN(market.unsettledMaintenanceAssetWeight);
159
+ break;
160
+ }
161
+
162
+ return assetWeight;
163
+ }
164
+
165
+ export function calculateMarketAvailablePNL(
166
+ market: MarketAccount,
167
+ bank: BankAccount
168
+ ): BN {
169
+ return getTokenAmount(market.pnlPool.balance, bank, BankBalanceType.DEPOSIT);
170
+ }
@@ -1,6 +1,11 @@
1
1
  import { AMM, OracleGuardRails } from '../types';
2
2
  import { OraclePriceData } from '../oracles/types';
3
- import { ONE, ZERO } from '../constants/numericConstants';
3
+ import {
4
+ BID_ASK_SPREAD_PRECISION,
5
+ MARK_PRICE_PRECISION,
6
+ ONE,
7
+ ZERO,
8
+ } from '../constants/numericConstants';
4
9
  import { BN } from '../index';
5
10
 
6
11
  export function isOracleValid(
@@ -9,7 +14,7 @@ export function isOracleValid(
9
14
  oracleGuardRails: OracleGuardRails,
10
15
  slot: number
11
16
  ): boolean {
12
- const isOraclePriceNonPositive = oraclePriceData.price.lt(ZERO);
17
+ const isOraclePriceNonPositive = oraclePriceData.price.lte(ZERO);
13
18
  const isOraclePriceTooVolatile =
14
19
  oraclePriceData.price
15
20
  .div(BN.max(ONE, amm.lastOraclePriceTwap))
@@ -18,9 +23,11 @@ export function isOracleValid(
18
23
  .div(BN.max(ONE, oraclePriceData.price))
19
24
  .gt(oracleGuardRails.validity.tooVolatileRatio);
20
25
 
21
- const isConfidenceTooLarge = oraclePriceData.price
22
- .div(BN.max(ONE, oraclePriceData.confidence))
23
- .lt(oracleGuardRails.validity.confidenceIntervalMaxSize);
26
+ const isConfidenceTooLarge = new BN(amm.baseSpread)
27
+ .add(BN.max(ONE, oraclePriceData.confidence))
28
+ .mul(BID_ASK_SPREAD_PRECISION)
29
+ .div(oraclePriceData.price)
30
+ .gt(new BN(amm.maxSpread));
24
31
 
25
32
  const oracleIsStale = oraclePriceData.slot
26
33
  .sub(new BN(slot))
@@ -34,3 +41,33 @@ export function isOracleValid(
34
41
  isConfidenceTooLarge
35
42
  );
36
43
  }
44
+
45
+ export function isOracleTooDivergent(
46
+ amm: AMM,
47
+ oraclePriceData: OraclePriceData,
48
+ oracleGuardRails: OracleGuardRails,
49
+ now: BN
50
+ ): boolean {
51
+ const sinceLastUpdate = now.sub(amm.lastOraclePriceTwapTs);
52
+ const sinceStart = BN.max(ZERO, new BN(60 * 5).sub(sinceLastUpdate));
53
+ const oracleTwap5min = amm.lastOraclePriceTwap5min
54
+ .mul(sinceStart)
55
+ .add(oraclePriceData.price)
56
+ .mul(sinceLastUpdate)
57
+ .div(sinceStart.add(sinceLastUpdate));
58
+
59
+ const oracleSpread = oracleTwap5min.sub(oraclePriceData.price);
60
+ const oracleSpreadPct = oracleSpread
61
+ .mul(MARK_PRICE_PRECISION)
62
+ .div(oracleTwap5min);
63
+
64
+ const tooDivergent = oracleSpreadPct
65
+ .abs()
66
+ .gte(
67
+ BID_ASK_SPREAD_PRECISION.mul(
68
+ oracleGuardRails.priceDivergence.markOracleDivergenceNumerator
69
+ ).div(oracleGuardRails.priceDivergence.markOracleDivergenceDenominator)
70
+ );
71
+
72
+ return tooDivergent;
73
+ }
@@ -18,6 +18,8 @@ import {
18
18
  getSwapDirection,
19
19
  } from './amm';
20
20
 
21
+ import { calculateMarginBaseAssetValue } from './margin';
22
+
21
23
  /**
22
24
  * calculateBaseAssetValue
23
25
  * = market value of closing entire position
@@ -84,6 +86,7 @@ export function calculateBaseAssetValue(
84
86
  * @param market
85
87
  * @param marketPosition
86
88
  * @param withFunding (adds unrealized funding payment pnl to result)
89
+ * @param oraclePriceData
87
90
  * @returns BaseAssetAmount : Precision QUOTE_PRECISION
88
91
  */
89
92
  export function calculatePositionPNL(
@@ -93,21 +96,21 @@ export function calculatePositionPNL(
93
96
  oraclePriceData: OraclePriceData
94
97
  ): BN {
95
98
  if (marketPosition.baseAssetAmount.eq(ZERO)) {
96
- return ZERO;
99
+ return marketPosition.quoteAssetAmount;
97
100
  }
98
101
 
99
- const baseAssetValue = calculateBaseAssetValue(
102
+ const baseAssetValue = calculateMarginBaseAssetValue(
100
103
  market,
101
104
  marketPosition,
102
105
  oraclePriceData
103
106
  );
104
107
 
105
- let pnl;
106
- if (marketPosition.baseAssetAmount.gt(ZERO)) {
107
- pnl = baseAssetValue.sub(marketPosition.quoteAssetAmount);
108
- } else {
109
- pnl = marketPosition.quoteAssetAmount.sub(baseAssetValue);
110
- }
108
+ const baseAssetValueSign = marketPosition.baseAssetAmount.isNeg()
109
+ ? new BN(-1)
110
+ : new BN(1);
111
+ let pnl = baseAssetValue
112
+ .mul(baseAssetValueSign)
113
+ .add(marketPosition.quoteAssetAmount);
111
114
 
112
115
  if (withFunding) {
113
116
  const fundingRatePnL = calculatePositionFundingPNL(
@@ -121,6 +124,36 @@ export function calculatePositionPNL(
121
124
  return pnl;
122
125
  }
123
126
 
127
+ export function calculateUnsettledPnl(
128
+ market: MarketAccount,
129
+ marketPosition: UserPosition,
130
+ oraclePriceData: OraclePriceData
131
+ ): BN {
132
+ const unrealizedPnl = calculatePositionPNL(
133
+ market,
134
+ marketPosition,
135
+ true,
136
+ oraclePriceData
137
+ );
138
+
139
+ let unsettledPnl = unrealizedPnl;
140
+ if (unrealizedPnl.gt(ZERO)) {
141
+ const fundingPnL = calculatePositionFundingPNL(market, marketPosition).div(
142
+ PRICE_TO_QUOTE_PRECISION
143
+ );
144
+
145
+ const maxPositivePnl = BN.max(
146
+ marketPosition.quoteAssetAmount
147
+ .add(marketPosition.quoteEntryAmount)
148
+ .add(fundingPnL),
149
+ ZERO
150
+ );
151
+
152
+ unsettledPnl = BN.min(maxPositivePnl, unrealizedPnl);
153
+ }
154
+ return unsettledPnl;
155
+ }
156
+
124
157
  /**
125
158
  *
126
159
  * @param market
@@ -156,7 +189,8 @@ export function positionIsAvailable(position: UserPosition): boolean {
156
189
  return (
157
190
  position.baseAssetAmount.eq(ZERO) &&
158
191
  position.openOrders.eq(ZERO) &&
159
- position.unsettledPnl.eq(ZERO)
192
+ position.quoteAssetAmount.eq(ZERO) &&
193
+ position.lpShares.eq(ZERO)
160
194
  );
161
195
  }
162
196
 
@@ -170,6 +204,23 @@ export function calculateEntryPrice(userPosition: UserPosition): BN {
170
204
  return ZERO;
171
205
  }
172
206
 
207
+ return userPosition.quoteEntryAmount
208
+ .mul(MARK_PRICE_PRECISION)
209
+ .mul(AMM_TO_QUOTE_PRECISION_RATIO)
210
+ .div(userPosition.baseAssetAmount)
211
+ .abs();
212
+ }
213
+
214
+ /**
215
+ *
216
+ * @param userPosition
217
+ * @returns Precision: MARK_PRICE_PRECISION (10^10)
218
+ */
219
+ export function calculateCostBasis(userPosition: UserPosition): BN {
220
+ if (userPosition.baseAssetAmount.eq(ZERO)) {
221
+ return ZERO;
222
+ }
223
+
173
224
  return userPosition.quoteAssetAmount
174
225
  .mul(MARK_PRICE_PRECISION)
175
226
  .mul(AMM_TO_QUOTE_PRECISION_RATIO)
package/src/math/trade.ts CHANGED
@@ -177,13 +177,13 @@ export function calculateTradeAcquiredAmounts(
177
177
 
178
178
  const acquiredBase = amm.baseAssetReserve.sub(newBaseAssetReserve);
179
179
  const acquiredQuote = amm.quoteAssetReserve.sub(newQuoteAssetReserve);
180
- const acquiredQuoteAssetamount = calculateQuoteAssetAmountSwapped(
180
+ const acquiredQuoteAssetAmount = calculateQuoteAssetAmountSwapped(
181
181
  acquiredQuote.abs(),
182
182
  amm.pegMultiplier,
183
183
  swapDirection
184
184
  );
185
185
 
186
- return [acquiredBase, acquiredQuote, acquiredQuoteAssetamount];
186
+ return [acquiredBase, acquiredQuote, acquiredQuoteAssetAmount];
187
187
  }
188
188
 
189
189
  /**