@drift-labs/sdk 0.2.0-master.3 → 0.2.0-master.30

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 (208) hide show
  1. package/README.md +13 -13
  2. package/lib/accounts/bulkUserStatsSubscription.d.ts +7 -0
  3. package/lib/accounts/bulkUserStatsSubscription.js +21 -0
  4. package/lib/accounts/bulkUserSubscription.js +0 -1
  5. package/lib/accounts/fetch.d.ts +2 -1
  6. package/lib/accounts/fetch.js +9 -1
  7. package/lib/accounts/pollingClearingHouseAccountSubscriber.d.ts +16 -16
  8. package/lib/accounts/pollingClearingHouseAccountSubscriber.js +31 -28
  9. package/lib/accounts/pollingUserStatsAccountSubscriber.d.ts +27 -0
  10. package/lib/accounts/pollingUserStatsAccountSubscriber.js +113 -0
  11. package/lib/accounts/types.d.ts +22 -11
  12. package/lib/accounts/webSocketClearingHouseAccountSubscriber.d.ts +17 -17
  13. package/lib/accounts/webSocketClearingHouseAccountSubscriber.js +37 -35
  14. package/lib/accounts/webSocketUserStatsAccountSubsriber.d.ts +20 -0
  15. package/lib/accounts/webSocketUserStatsAccountSubsriber.js +47 -0
  16. package/lib/addresses/marketAddresses.d.ts +1 -3
  17. package/lib/addresses/pda.d.ts +10 -4
  18. package/lib/addresses/pda.js +51 -14
  19. package/lib/admin.d.ts +34 -22
  20. package/lib/admin.js +182 -73
  21. package/lib/clearingHouse.d.ts +120 -42
  22. package/lib/clearingHouse.js +1505 -254
  23. package/lib/clearingHouseConfig.d.ts +4 -4
  24. package/lib/clearingHouseUser.d.ts +50 -38
  25. package/lib/clearingHouseUser.js +410 -190
  26. package/lib/clearingHouseUserStats.d.ts +18 -0
  27. package/lib/clearingHouseUserStats.js +49 -0
  28. package/lib/clearingHouseUserStatsConfig.d.ts +14 -0
  29. package/lib/clearingHouseUserStatsConfig.js +2 -0
  30. package/lib/config.d.ts +7 -9
  31. package/lib/config.js +21 -21
  32. package/lib/constants/numericConstants.d.ts +18 -12
  33. package/lib/constants/numericConstants.js +28 -21
  34. package/lib/constants/perpMarkets.d.ts +18 -0
  35. package/lib/constants/{markets.js → perpMarkets.js} +7 -7
  36. package/lib/constants/spotMarkets.d.ts +19 -0
  37. package/lib/constants/spotMarkets.js +53 -0
  38. package/lib/dlob/DLOB.d.ts +73 -0
  39. package/lib/dlob/DLOB.js +553 -0
  40. package/lib/dlob/DLOBNode.d.ts +52 -0
  41. package/lib/dlob/DLOBNode.js +82 -0
  42. package/lib/dlob/NodeList.d.ts +26 -0
  43. package/lib/dlob/NodeList.js +138 -0
  44. package/lib/events/eventList.js +3 -0
  45. package/lib/events/eventSubscriber.d.ts +4 -2
  46. package/lib/events/eventSubscriber.js +16 -9
  47. package/lib/events/fetchLogs.d.ts +10 -1
  48. package/lib/events/fetchLogs.js +27 -7
  49. package/lib/events/pollingLogProvider.d.ts +2 -1
  50. package/lib/events/pollingLogProvider.js +6 -2
  51. package/lib/events/sort.js +8 -11
  52. package/lib/events/types.d.ts +7 -2
  53. package/lib/events/types.js +5 -0
  54. package/lib/examples/makeTradeExample.js +20 -8
  55. package/lib/factory/bigNum.d.ts +1 -0
  56. package/lib/factory/bigNum.js +34 -10
  57. package/lib/idl/clearing_house.json +4313 -1433
  58. package/lib/idl/{mock_usdc_faucet.json → token_faucet.json} +46 -23
  59. package/lib/index.d.ts +21 -6
  60. package/lib/index.js +25 -6
  61. package/lib/math/amm.d.ts +8 -5
  62. package/lib/math/amm.js +68 -46
  63. package/lib/math/auction.js +4 -1
  64. package/lib/math/conversion.js +1 -1
  65. package/lib/math/funding.d.ts +6 -6
  66. package/lib/math/funding.js +11 -10
  67. package/lib/math/insurance.d.ts +4 -0
  68. package/lib/math/insurance.js +27 -0
  69. package/lib/math/margin.d.ts +11 -0
  70. package/lib/math/margin.js +77 -0
  71. package/lib/math/market.d.ts +14 -9
  72. package/lib/math/market.js +69 -7
  73. package/lib/math/oracles.d.ts +4 -0
  74. package/lib/math/oracles.js +36 -8
  75. package/lib/math/orders.d.ts +6 -2
  76. package/lib/math/orders.js +78 -3
  77. package/lib/math/position.d.ts +21 -13
  78. package/lib/math/position.js +76 -36
  79. package/lib/math/repeg.js +14 -5
  80. package/lib/math/spotBalance.d.ts +22 -0
  81. package/lib/math/spotBalance.js +193 -0
  82. package/lib/math/spotMarket.d.ts +4 -0
  83. package/lib/math/spotMarket.js +8 -0
  84. package/lib/math/spotPosition.d.ts +6 -0
  85. package/lib/math/spotPosition.js +23 -0
  86. package/lib/math/trade.d.ts +10 -10
  87. package/lib/math/trade.js +22 -29
  88. package/lib/oracles/pythClient.js +1 -1
  89. package/lib/oracles/quoteAssetOracleClient.js +1 -1
  90. package/lib/oracles/switchboardClient.js +1 -1
  91. package/lib/orderParams.d.ts +14 -5
  92. package/lib/orderParams.js +12 -92
  93. package/lib/serum/serumSubscriber.d.ts +23 -0
  94. package/lib/serum/serumSubscriber.js +41 -0
  95. package/lib/serum/types.d.ts +11 -0
  96. package/lib/serum/types.js +2 -0
  97. package/lib/slot/SlotSubscriber.d.ts +7 -0
  98. package/lib/slot/SlotSubscriber.js +3 -0
  99. package/lib/{mockUSDCFaucet.d.ts → tokenFaucet.d.ts} +8 -5
  100. package/lib/{mockUSDCFaucet.js → tokenFaucet.js} +63 -51
  101. package/lib/tx/retryTxSender.d.ts +1 -1
  102. package/lib/tx/retryTxSender.js +13 -4
  103. package/lib/tx/types.d.ts +1 -1
  104. package/lib/tx/utils.js +1 -1
  105. package/lib/types.d.ts +474 -123
  106. package/lib/types.js +99 -5
  107. package/lib/userMap/userMap.d.ts +25 -0
  108. package/lib/userMap/userMap.js +73 -0
  109. package/lib/userMap/userStatsMap.d.ts +19 -0
  110. package/lib/userMap/userStatsMap.js +68 -0
  111. package/lib/util/computeUnits.js +1 -1
  112. package/lib/util/getTokenAddress.d.ts +2 -0
  113. package/lib/util/getTokenAddress.js +9 -0
  114. package/package.json +9 -5
  115. package/src/accounts/bulkUserStatsSubscription.ts +33 -0
  116. package/src/accounts/bulkUserSubscription.ts +0 -1
  117. package/src/accounts/fetch.ts +27 -2
  118. package/src/accounts/pollingClearingHouseAccountSubscriber.ts +46 -42
  119. package/src/accounts/pollingUserStatsAccountSubscriber.ts +172 -0
  120. package/src/accounts/types.ts +31 -11
  121. package/src/accounts/webSocketClearingHouseAccountSubscriber.ts +64 -59
  122. package/src/accounts/webSocketUserStatsAccountSubsriber.ts +80 -0
  123. package/src/addresses/marketAddresses.ts +1 -2
  124. package/src/addresses/pda.ts +88 -14
  125. package/src/admin.ts +333 -128
  126. package/src/assert/assert.js +9 -0
  127. package/src/clearingHouse.ts +2464 -458
  128. package/src/clearingHouseConfig.ts +4 -3
  129. package/src/clearingHouseUser.ts +747 -291
  130. package/src/clearingHouseUserStats.ts +75 -0
  131. package/src/clearingHouseUserStatsConfig.ts +18 -0
  132. package/src/config.ts +30 -31
  133. package/src/constants/numericConstants.ts +41 -25
  134. package/src/constants/{markets.ts → perpMarkets.ts} +10 -10
  135. package/src/constants/spotMarkets.ts +72 -0
  136. package/src/dlob/DLOB.ts +868 -0
  137. package/src/dlob/DLOBNode.ts +162 -0
  138. package/src/dlob/NodeList.ts +185 -0
  139. package/src/events/eventList.js +77 -0
  140. package/src/events/eventList.ts +3 -0
  141. package/src/events/eventSubscriber.ts +20 -12
  142. package/src/events/fetchLogs.ts +35 -8
  143. package/src/events/pollingLogProvider.ts +10 -2
  144. package/src/events/sort.ts +11 -15
  145. package/src/events/types.ts +16 -1
  146. package/src/examples/makeTradeExample.js +157 -0
  147. package/src/examples/makeTradeExample.ts +32 -14
  148. package/src/factory/bigNum.ts +42 -13
  149. package/src/idl/clearing_house.json +4313 -1433
  150. package/src/idl/{mock_usdc_faucet.json → token_faucet.json} +46 -23
  151. package/src/index.ts +21 -6
  152. package/src/math/amm.ts +136 -66
  153. package/src/math/auction.ts +5 -1
  154. package/src/math/conversion.ts +2 -2
  155. package/src/math/funding.ts +20 -18
  156. package/src/math/insurance.ts +35 -0
  157. package/src/math/margin.ts +127 -0
  158. package/src/math/market.ts +138 -12
  159. package/src/math/oracles.ts +63 -9
  160. package/src/math/orders.ts +138 -4
  161. package/src/math/position.ts +119 -58
  162. package/src/math/repeg.ts +16 -6
  163. package/src/math/spotBalance.ts +316 -0
  164. package/src/math/spotMarket.ts +9 -0
  165. package/src/math/spotPosition.ts +47 -0
  166. package/src/math/trade.ts +43 -49
  167. package/src/oracles/pythClient.ts +2 -2
  168. package/src/oracles/quoteAssetOracleClient.ts +2 -2
  169. package/src/oracles/switchboardClient.ts +2 -2
  170. package/src/orderParams.ts +24 -137
  171. package/src/serum/serumSubscriber.ts +80 -0
  172. package/src/serum/types.ts +13 -0
  173. package/src/slot/SlotSubscriber.ts +11 -1
  174. package/src/token/index.js +38 -0
  175. package/src/{mockUSDCFaucet.ts → tokenFaucet.ts} +82 -70
  176. package/src/tx/retryTxSender.ts +16 -5
  177. package/src/tx/types.js +2 -0
  178. package/src/tx/types.ts +2 -1
  179. package/src/tx/utils.js +17 -0
  180. package/src/tx/utils.ts +1 -1
  181. package/src/types.ts +477 -125
  182. package/src/userMap/userMap.ts +100 -0
  183. package/src/userMap/userStatsMap.ts +110 -0
  184. package/src/util/computeUnits.js +21 -11
  185. package/src/util/computeUnits.ts +1 -1
  186. package/src/util/getTokenAddress.js +9 -0
  187. package/src/util/getTokenAddress.ts +18 -0
  188. package/src/util/promiseTimeout.js +14 -0
  189. package/src/util/tps.js +27 -0
  190. package/tests/bn/test.ts +12 -3
  191. package/tests/dlob/helpers.ts +374 -0
  192. package/tests/dlob/test.ts +2865 -0
  193. package/lib/constants/banks.d.ts +0 -16
  194. package/lib/constants/banks.js +0 -34
  195. package/lib/constants/markets.d.ts +0 -19
  196. package/lib/math/bankBalance.d.ts +0 -9
  197. package/lib/math/bankBalance.js +0 -75
  198. package/lib/math/state.d.ts +0 -8
  199. package/lib/math/state.js +0 -15
  200. package/lib/orders.d.ts +0 -8
  201. package/lib/orders.js +0 -134
  202. package/src/constants/banks.ts +0 -43
  203. package/src/math/bankBalance.ts +0 -112
  204. package/src/math/state.ts +0 -14
  205. package/src/math/utils.js +0 -27
  206. package/src/math/utils.js.map +0 -1
  207. package/src/orders.ts +0 -244
  208. package/src/util/computeUnits.js.map +0 -1
@@ -5,9 +5,11 @@ const types_1 = require("./types");
5
5
  const position_1 = require("./math/position");
6
6
  const numericConstants_1 = require("./constants/numericConstants");
7
7
  const _1 = require(".");
8
- const bankBalance_1 = require("./math/bankBalance");
8
+ const spotBalance_1 = require("./math/spotBalance");
9
+ const margin_1 = require("./math/margin");
9
10
  const pollingUserAccountSubscriber_1 = require("./accounts/pollingUserAccountSubscriber");
10
11
  const webSocketUserAccountSubscriber_1 = require("./accounts/webSocketUserAccountSubscriber");
12
+ const spotPosition_1 = require("./math/spotPosition");
11
13
  class ClearingHouseUser {
12
14
  constructor(config) {
13
15
  var _a;
@@ -58,21 +60,30 @@ class ClearingHouseUser {
58
60
  * @returns userPosition
59
61
  */
60
62
  getUserPosition(marketIndex) {
61
- return this.getUserAccount().positions.find((position) => position.marketIndex.eq(marketIndex));
63
+ return this.getUserAccount().perpPositions.find((position) => position.marketIndex === marketIndex);
62
64
  }
63
65
  getEmptyPosition(marketIndex) {
64
66
  return {
65
67
  baseAssetAmount: numericConstants_1.ZERO,
68
+ remainderBaseAssetAmount: numericConstants_1.ZERO,
66
69
  lastCumulativeFundingRate: numericConstants_1.ZERO,
67
70
  marketIndex,
68
71
  quoteAssetAmount: numericConstants_1.ZERO,
69
72
  quoteEntryAmount: numericConstants_1.ZERO,
70
- openOrders: numericConstants_1.ZERO,
71
- unsettledPnl: numericConstants_1.ZERO,
73
+ openOrders: 0,
72
74
  openBids: numericConstants_1.ZERO,
73
75
  openAsks: numericConstants_1.ZERO,
76
+ settledPnl: numericConstants_1.ZERO,
77
+ lpShares: numericConstants_1.ZERO,
78
+ lastFeePerLp: numericConstants_1.ZERO,
79
+ lastNetBaseAssetAmountPerLp: numericConstants_1.ZERO,
80
+ lastNetQuoteAssetAmountPerLp: numericConstants_1.ZERO,
74
81
  };
75
82
  }
83
+ getClonedPosition(position) {
84
+ const clonedPosition = Object.assign({}, position);
85
+ return clonedPosition;
86
+ }
76
87
  /**
77
88
  * @param orderId
78
89
  * @returns Order
@@ -94,6 +105,92 @@ class ClearingHouseUser {
94
105
  const userAccountRPCResponse = await this.clearingHouse.connection.getParsedAccountInfo(this.userAccountPublicKey);
95
106
  return userAccountRPCResponse.value !== null;
96
107
  }
108
+ /**
109
+ * calculates the market position if the lp position was settled
110
+ * @returns : the settled userPosition
111
+ * @returns : the dust base asset amount (ie, < stepsize)
112
+ * @returns : pnl from settle
113
+ */
114
+ getSettledLPPosition(marketIndex) {
115
+ const _position = this.getUserPosition(marketIndex);
116
+ const position = this.getClonedPosition(_position);
117
+ const market = this.clearingHouse.getPerpMarketAccount(position.marketIndex);
118
+ const nShares = position.lpShares;
119
+ const deltaBaa = market.amm.marketPositionPerLp.baseAssetAmount
120
+ .sub(position.lastNetBaseAssetAmountPerLp)
121
+ .mul(nShares)
122
+ .div(numericConstants_1.AMM_RESERVE_PRECISION);
123
+ const deltaQaa = market.amm.marketPositionPerLp.quoteAssetAmount
124
+ .sub(position.lastNetQuoteAssetAmountPerLp)
125
+ .mul(nShares)
126
+ .div(numericConstants_1.AMM_RESERVE_PRECISION);
127
+ function sign(v) {
128
+ const sign = { true: new _1.BN(1), false: new _1.BN(-1) }[v.gte(numericConstants_1.ZERO).toString()];
129
+ return sign;
130
+ }
131
+ function standardize(amount, stepsize) {
132
+ const remainder = amount.abs().mod(stepsize).mul(sign(amount));
133
+ const standardizedAmount = amount.sub(remainder);
134
+ return [standardizedAmount, remainder];
135
+ }
136
+ const [standardizedBaa, remainderBaa] = standardize(deltaBaa, market.amm.baseAssetAmountStepSize);
137
+ position.remainderBaseAssetAmount =
138
+ position.remainderBaseAssetAmount.add(remainderBaa);
139
+ if (position.remainderBaseAssetAmount
140
+ .abs()
141
+ .gte(market.amm.baseAssetAmountStepSize)) {
142
+ const [newStandardizedBaa, newRemainderBaa] = standardize(position.remainderBaseAssetAmount, market.amm.baseAssetAmountStepSize);
143
+ position.baseAssetAmount =
144
+ position.baseAssetAmount.add(newStandardizedBaa);
145
+ position.remainderBaseAssetAmount = newRemainderBaa;
146
+ }
147
+ let updateType;
148
+ if (position.baseAssetAmount.eq(numericConstants_1.ZERO)) {
149
+ updateType = 'open';
150
+ }
151
+ else if (sign(position.baseAssetAmount).eq(sign(deltaBaa))) {
152
+ updateType = 'increase';
153
+ }
154
+ else if (position.baseAssetAmount.abs().gt(deltaBaa.abs())) {
155
+ updateType = 'reduce';
156
+ }
157
+ else if (position.baseAssetAmount.abs().eq(deltaBaa.abs())) {
158
+ updateType = 'close';
159
+ }
160
+ else {
161
+ updateType = 'flip';
162
+ }
163
+ let newQuoteEntry;
164
+ let pnl;
165
+ if (updateType == 'open' || updateType == 'increase') {
166
+ newQuoteEntry = position.quoteEntryAmount.add(deltaQaa);
167
+ pnl = 0;
168
+ }
169
+ else if (updateType == 'reduce' || updateType == 'close') {
170
+ newQuoteEntry = position.quoteEntryAmount.sub(position.quoteEntryAmount
171
+ .mul(deltaBaa.abs())
172
+ .div(position.baseAssetAmount.abs()));
173
+ pnl = position.quoteEntryAmount.sub(newQuoteEntry).add(deltaQaa);
174
+ }
175
+ else {
176
+ newQuoteEntry = deltaQaa.sub(deltaQaa.mul(position.baseAssetAmount.abs()).div(deltaBaa.abs()));
177
+ pnl = position.quoteEntryAmount.add(deltaQaa.sub(newQuoteEntry));
178
+ }
179
+ position.quoteEntryAmount = newQuoteEntry;
180
+ position.baseAssetAmount = position.baseAssetAmount.add(standardizedBaa);
181
+ position.quoteAssetAmount = position.quoteAssetAmount.add(deltaQaa);
182
+ if (position.baseAssetAmount.gt(numericConstants_1.ZERO)) {
183
+ position.lastCumulativeFundingRate = market.amm.cumulativeFundingRateLong;
184
+ }
185
+ else if (position.baseAssetAmount.lt(numericConstants_1.ZERO)) {
186
+ position.lastCumulativeFundingRate =
187
+ market.amm.cumulativeFundingRateShort;
188
+ }
189
+ else {
190
+ position.lastCumulativeFundingRate = numericConstants_1.ZERO;
191
+ }
192
+ return [position, remainderBaa, pnl];
193
+ }
97
194
  /**
98
195
  * calculates Buying Power = FC * MAX_LEVERAGE
99
196
  * @returns : Precision QUOTE_PRECISION
@@ -113,50 +210,44 @@ class ClearingHouseUser {
113
210
  const freeCollateral = totalCollateral.sub(initialMarginRequirement);
114
211
  return freeCollateral.gte(numericConstants_1.ZERO) ? freeCollateral : numericConstants_1.ZERO;
115
212
  }
116
- getInitialMarginRequirement() {
117
- return this.getUserAccount()
118
- .positions.reduce((marginRequirement, marketPosition) => {
119
- const market = this.clearingHouse.getMarketAccount(marketPosition.marketIndex);
120
- return marginRequirement.add((0, _1.calculateBaseAssetValue)(market, marketPosition, this.getOracleDataForMarket(market.marketIndex))
121
- .mul(new _1.BN(market.marginRatioInitial))
122
- .div(numericConstants_1.MARGIN_PRECISION));
123
- }, numericConstants_1.ZERO)
124
- .add(this.getTotalLiability());
213
+ /**
214
+ * @returns The margin requirement of a certain type (Initial or Maintenance) in USDC. : QUOTE_PRECISION
215
+ */
216
+ getMarginRequirement(marginCategory, liquidationBuffer) {
217
+ return this.getTotalPerpPositionValue(marginCategory, liquidationBuffer, true).add(this.getSpotMarketLiabilityValue(undefined, marginCategory, liquidationBuffer, true));
125
218
  }
126
219
  /**
127
- * @returns The partial margin requirement in USDC. : QUOTE_PRECISION
220
+ * @returns The initial margin requirement in USDC. : QUOTE_PRECISION
128
221
  */
129
- getPartialMarginRequirement() {
130
- return this.getUserAccount()
131
- .positions.reduce((marginRequirement, marketPosition) => {
132
- const market = this.clearingHouse.getMarketAccount(marketPosition.marketIndex);
133
- return marginRequirement.add((0, _1.calculateBaseAssetValue)(market, marketPosition, this.getOracleDataForMarket(market.marketIndex))
134
- .mul(new _1.BN(market.marginRatioPartial))
135
- .div(numericConstants_1.MARGIN_PRECISION));
136
- }, numericConstants_1.ZERO)
137
- .add(this.getTotalLiability());
222
+ getInitialMarginRequirement() {
223
+ return this.getMarginRequirement('Initial');
138
224
  }
139
225
  /**
140
- * calculates unrealized position price pnl
141
- * @returns : Precision QUOTE_PRECISION
226
+ * @returns The maintenance margin requirement in USDC. : QUOTE_PRECISION
142
227
  */
143
- getUnrealizedPNL(withFunding, marketIndex) {
144
- return this.getUserAccount()
145
- .positions.filter((pos) => marketIndex ? pos.marketIndex === marketIndex : true)
146
- .reduce((pnl, marketPosition) => {
147
- const market = this.clearingHouse.getMarketAccount(marketPosition.marketIndex);
148
- return pnl.add((0, _1.calculatePositionPNL)(market, marketPosition, withFunding, this.getOracleDataForMarket(market.marketIndex)));
149
- }, numericConstants_1.ZERO);
228
+ getMaintenanceMarginRequirement(liquidationBuffer) {
229
+ return this.getMarginRequirement('Maintenance', liquidationBuffer);
150
230
  }
151
231
  /**
152
232
  * calculates unrealized position price pnl
153
233
  * @returns : Precision QUOTE_PRECISION
154
234
  */
155
- getUnsettledPNL(marketIndex) {
235
+ getUnrealizedPNL(withFunding, marketIndex, withWeightMarginCategory) {
236
+ const quoteSpotMarket = this.clearingHouse.getQuoteSpotMarketAccount();
156
237
  return this.getUserAccount()
157
- .positions.filter((pos) => marketIndex ? pos.marketIndex === marketIndex : true)
158
- .reduce((pnl, marketPosition) => {
159
- return pnl.add(marketPosition.unsettledPnl);
238
+ .perpPositions.filter((pos) => marketIndex ? pos.marketIndex === marketIndex : true)
239
+ .reduce((unrealizedPnl, perpPosition) => {
240
+ const market = this.clearingHouse.getPerpMarketAccount(perpPosition.marketIndex);
241
+ const oraclePriceData = this.getOracleDataForMarket(market.marketIndex);
242
+ let positionUnrealizedPnl = (0, _1.calculatePositionPNL)(market, perpPosition, withFunding, oraclePriceData);
243
+ if (withWeightMarginCategory !== undefined) {
244
+ if (positionUnrealizedPnl.gt(numericConstants_1.ZERO)) {
245
+ positionUnrealizedPnl = positionUnrealizedPnl
246
+ .mul((0, _1.calculateUnrealizedAssetWeight)(market, quoteSpotMarket, positionUnrealizedPnl, withWeightMarginCategory, oraclePriceData))
247
+ .div(new _1.BN(numericConstants_1.SPOT_MARKET_WEIGHT_PRECISION));
248
+ }
249
+ }
250
+ return unrealizedPnl.add(positionUnrealizedPnl);
160
251
  }, numericConstants_1.ZERO);
161
252
  }
162
253
  /**
@@ -165,87 +256,218 @@ class ClearingHouseUser {
165
256
  */
166
257
  getUnrealizedFundingPNL(marketIndex) {
167
258
  return this.getUserAccount()
168
- .positions.filter((pos) => marketIndex ? pos.marketIndex === marketIndex : true)
169
- .reduce((pnl, marketPosition) => {
170
- const market = this.clearingHouse.getMarketAccount(marketPosition.marketIndex);
171
- return pnl.add((0, _1.calculatePositionFundingPNL)(market, marketPosition));
259
+ .perpPositions.filter((pos) => marketIndex ? pos.marketIndex === marketIndex : true)
260
+ .reduce((pnl, perpPosition) => {
261
+ const market = this.clearingHouse.getPerpMarketAccount(perpPosition.marketIndex);
262
+ return pnl.add((0, _1.calculatePositionFundingPNL)(market, perpPosition));
172
263
  }, numericConstants_1.ZERO);
173
264
  }
174
- getTotalLiability() {
175
- return this.getUserAccount().bankBalances.reduce((totalAssetValue, bankBalance) => {
176
- if (bankBalance.balance.eq(numericConstants_1.ZERO) ||
177
- (0, types_1.isVariant)(bankBalance.balanceType, 'deposit')) {
178
- return totalAssetValue;
265
+ getSpotMarketLiabilityValue(marketIndex, marginCategory, liquidationBuffer, includeOpenOrders) {
266
+ return this.getUserAccount().spotPositions.reduce((totalLiabilityValue, spotPosition) => {
267
+ if ((0, spotPosition_1.isSpotPositionAvailable)(spotPosition) ||
268
+ (marketIndex !== undefined &&
269
+ spotPosition.marketIndex !== marketIndex)) {
270
+ return totalLiabilityValue;
179
271
  }
180
- // Todo this needs to account for whether it's based on initial or maintenance requirements
181
- const bankAccount = this.clearingHouse.getBankAccount(bankBalance.bankIndex);
182
- const tokenAmount = (0, bankBalance_1.getTokenAmount)(bankBalance.balance, bankAccount, bankBalance.balanceType);
183
- return totalAssetValue.add(tokenAmount
184
- .mul(this.getOracleDataForBank(bankAccount.bankIndex).price)
185
- .mul(bankAccount.initialLiabilityWeight)
186
- .div(numericConstants_1.BANK_WEIGHT_PRECISION)
187
- .div(numericConstants_1.MARK_PRICE_PRECISION));
272
+ const spotMarketAccount = this.clearingHouse.getSpotMarketAccount(spotPosition.marketIndex);
273
+ if (spotPosition.marketIndex === numericConstants_1.QUOTE_SPOT_MARKET_INDEX) {
274
+ if ((0, types_1.isVariant)(spotPosition.balanceType, 'borrow')) {
275
+ const tokenAmount = (0, spotBalance_1.getTokenAmount)(spotPosition.balance, spotMarketAccount, spotPosition.balanceType);
276
+ let weight = numericConstants_1.SPOT_MARKET_WEIGHT_PRECISION;
277
+ if (marginCategory === 'Initial') {
278
+ weight = _1.BN.max(weight, new _1.BN(this.getUserAccount().customMarginRatio));
279
+ }
280
+ const weightedTokenValue = tokenAmount
281
+ .mul(weight)
282
+ .div(numericConstants_1.SPOT_MARKET_WEIGHT_PRECISION);
283
+ return totalLiabilityValue.add(weightedTokenValue);
284
+ }
285
+ else {
286
+ return totalLiabilityValue;
287
+ }
288
+ }
289
+ const oraclePriceData = this.getOracleDataForSpotMarket(spotPosition.marketIndex);
290
+ if (!includeOpenOrders) {
291
+ if ((0, types_1.isVariant)(spotPosition.balanceType, 'borrow')) {
292
+ const tokenAmount = (0, spotBalance_1.getTokenAmount)(spotPosition.balance, spotMarketAccount, spotPosition.balanceType);
293
+ const liabilityValue = this.getSpotLiabilityValue(tokenAmount, oraclePriceData, spotMarketAccount, marginCategory, liquidationBuffer);
294
+ return totalLiabilityValue.add(liabilityValue);
295
+ }
296
+ else {
297
+ return totalLiabilityValue;
298
+ }
299
+ }
300
+ const [worstCaseTokenAmount, worstCaseQuoteTokenAmount] = (0, spotPosition_1.getWorstCaseTokenAmounts)(spotPosition, spotMarketAccount, this.getOracleDataForSpotMarket(spotPosition.marketIndex));
301
+ let newTotalLiabilityValue = totalLiabilityValue;
302
+ if (worstCaseTokenAmount.lt(numericConstants_1.ZERO)) {
303
+ const baseLiabilityValue = this.getSpotLiabilityValue(worstCaseTokenAmount, oraclePriceData, spotMarketAccount, marginCategory, liquidationBuffer);
304
+ newTotalLiabilityValue =
305
+ newTotalLiabilityValue.add(baseLiabilityValue);
306
+ }
307
+ if (worstCaseQuoteTokenAmount.lt(numericConstants_1.ZERO)) {
308
+ let weight = numericConstants_1.SPOT_MARKET_WEIGHT_PRECISION;
309
+ if (marginCategory === 'Initial') {
310
+ weight = _1.BN.max(weight, new _1.BN(this.getUserAccount().customMarginRatio));
311
+ }
312
+ const weightedTokenValue = worstCaseQuoteTokenAmount
313
+ .mul(weight)
314
+ .div(numericConstants_1.SPOT_MARKET_WEIGHT_PRECISION);
315
+ newTotalLiabilityValue =
316
+ newTotalLiabilityValue.add(weightedTokenValue);
317
+ }
318
+ return newTotalLiabilityValue;
188
319
  }, numericConstants_1.ZERO);
189
320
  }
190
- getCollateralValue(bankIndex) {
191
- return this.getUserAccount().bankBalances.reduce((totalAssetValue, bankBalance) => {
192
- if (bankBalance.balance.eq(numericConstants_1.ZERO) ||
193
- (bankIndex !== undefined && !bankBalance.bankIndex.eq(bankIndex))) {
321
+ getSpotLiabilityValue(tokenAmount, oraclePriceData, spotMarketAccount, marginCategory, liquidationBuffer) {
322
+ let liabilityValue = (0, _1.getTokenValue)(tokenAmount, spotMarketAccount.decimals, oraclePriceData);
323
+ if (marginCategory !== undefined) {
324
+ let weight = (0, spotBalance_1.calculateLiabilityWeight)(tokenAmount, spotMarketAccount, marginCategory);
325
+ if (marginCategory === 'Initial') {
326
+ weight = _1.BN.max(weight, new _1.BN(this.getUserAccount().customMarginRatio));
327
+ }
328
+ if (liquidationBuffer !== undefined) {
329
+ weight = weight.add(liquidationBuffer);
330
+ }
331
+ liabilityValue = liabilityValue
332
+ .mul(weight)
333
+ .div(numericConstants_1.SPOT_MARKET_WEIGHT_PRECISION);
334
+ }
335
+ return liabilityValue;
336
+ }
337
+ getSpotMarketAssetValue(marketIndex, marginCategory, includeOpenOrders) {
338
+ return this.getUserAccount().spotPositions.reduce((totalAssetValue, spotPosition) => {
339
+ if ((0, spotPosition_1.isSpotPositionAvailable)(spotPosition) ||
340
+ (marketIndex !== undefined &&
341
+ spotPosition.marketIndex !== marketIndex)) {
194
342
  return totalAssetValue;
195
343
  }
196
344
  // Todo this needs to account for whether it's based on initial or maintenance requirements
197
- const bankAccount = this.clearingHouse.getBankAccount(bankBalance.bankIndex);
198
- let tokenAmount = (0, bankBalance_1.getTokenAmount)(bankBalance.balance, bankAccount, bankBalance.balanceType);
199
- if ((0, types_1.isVariant)(bankBalance.balanceType, 'borrow')) {
200
- tokenAmount = tokenAmount.mul(new _1.BN(-1));
345
+ const spotMarketAccount = this.clearingHouse.getSpotMarketAccount(spotPosition.marketIndex);
346
+ if (spotPosition.marketIndex === numericConstants_1.QUOTE_SPOT_MARKET_INDEX) {
347
+ if ((0, types_1.isVariant)(spotPosition.balanceType, 'deposit')) {
348
+ const tokenAmount = (0, spotBalance_1.getTokenAmount)(spotPosition.balance, spotMarketAccount, spotPosition.balanceType);
349
+ return totalAssetValue.add(tokenAmount);
350
+ }
351
+ else {
352
+ return totalAssetValue;
353
+ }
354
+ }
355
+ const oraclePriceData = this.getOracleDataForSpotMarket(spotPosition.marketIndex);
356
+ if (!includeOpenOrders) {
357
+ if ((0, types_1.isVariant)(spotPosition.balanceType, 'deposit')) {
358
+ const tokenAmount = (0, spotBalance_1.getTokenAmount)(spotPosition.balance, spotMarketAccount, spotPosition.balanceType);
359
+ const assetValue = this.getSpotAssetValue(tokenAmount, oraclePriceData, spotMarketAccount, marginCategory);
360
+ return totalAssetValue.add(assetValue);
361
+ }
362
+ else {
363
+ return totalAssetValue;
364
+ }
201
365
  }
202
- return totalAssetValue.add(tokenAmount
203
- .mul(this.getOracleDataForBank(bankAccount.bankIndex).price)
204
- .div(numericConstants_1.MARK_PRICE_PRECISION));
366
+ const [worstCaseTokenAmount, worstCaseQuoteTokenAmount] = (0, spotPosition_1.getWorstCaseTokenAmounts)(spotPosition, spotMarketAccount, this.getOracleDataForSpotMarket(spotPosition.marketIndex));
367
+ let newTotalAssetValue = totalAssetValue;
368
+ if (worstCaseTokenAmount.gt(numericConstants_1.ZERO)) {
369
+ const baseAssetValue = this.getSpotAssetValue(worstCaseTokenAmount, oraclePriceData, spotMarketAccount, marginCategory);
370
+ newTotalAssetValue = newTotalAssetValue.add(baseAssetValue);
371
+ }
372
+ if (worstCaseQuoteTokenAmount.gt(numericConstants_1.ZERO)) {
373
+ newTotalAssetValue = newTotalAssetValue.add(worstCaseQuoteTokenAmount);
374
+ }
375
+ return newTotalAssetValue;
205
376
  }, numericConstants_1.ZERO);
206
377
  }
378
+ getSpotAssetValue(tokenAmount, oraclePriceData, spotMarketAccount, marginCategory) {
379
+ let assetValue = (0, _1.getTokenValue)(tokenAmount, spotMarketAccount.decimals, oraclePriceData);
380
+ if (marginCategory !== undefined) {
381
+ const weight = (0, spotBalance_1.calculateAssetWeight)(tokenAmount, spotMarketAccount, marginCategory);
382
+ assetValue = assetValue.mul(weight).div(numericConstants_1.SPOT_MARKET_WEIGHT_PRECISION);
383
+ }
384
+ return assetValue;
385
+ }
386
+ getNetSpotMarketValue(withWeightMarginCategory) {
387
+ return this.getSpotMarketAssetValue(undefined, withWeightMarginCategory).sub(this.getSpotMarketLiabilityValue(undefined, withWeightMarginCategory));
388
+ }
207
389
  /**
208
390
  * calculates TotalCollateral: collateral + unrealized pnl
209
- * TODO: rename to total equity (for perpetuals swaps)
210
391
  * @returns : Precision QUOTE_PRECISION
211
392
  */
212
- getTotalCollateral() {
213
- return this.getUserAccount()
214
- .bankBalances.reduce((totalAssetValue, bankBalance) => {
215
- if (bankBalance.balance.eq(numericConstants_1.ZERO) ||
216
- (0, types_1.isVariant)(bankBalance.balanceType, 'borrow')) {
217
- return totalAssetValue;
218
- }
219
- // Todo this needs to account for whether it's based on initial or maintenance requirements
220
- const bankAccount = this.clearingHouse.getBankAccount(bankBalance.bankIndex);
221
- const tokenAmount = (0, bankBalance_1.getTokenAmount)(bankBalance.balance, bankAccount, bankBalance.balanceType);
222
- return totalAssetValue.add(tokenAmount
223
- .mul(this.getOracleDataForBank(bankAccount.bankIndex).price)
224
- .mul(bankAccount.initialAssetWeight)
225
- .div(numericConstants_1.BANK_WEIGHT_PRECISION)
226
- .div(numericConstants_1.MARK_PRICE_PRECISION));
227
- }, numericConstants_1.ZERO)
228
- .add(this.getUnrealizedPNL(true))
229
- .add(this.getUnsettledPNL());
393
+ getTotalCollateral(marginCategory = 'Initial') {
394
+ return this.getSpotMarketAssetValue(undefined, marginCategory, true).add(this.getUnrealizedPNL(true, undefined, marginCategory));
230
395
  }
231
396
  /**
232
- * calculates sum of position value across all positions
397
+ * calculates sum of position value across all positions in margin system
233
398
  * @returns : Precision QUOTE_PRECISION
234
399
  */
235
- getTotalPositionValue() {
236
- return this.getUserAccount().positions.reduce((positionValue, marketPosition) => {
237
- const market = this.clearingHouse.getMarketAccount(marketPosition.marketIndex);
238
- return positionValue.add((0, _1.calculateBaseAssetValue)(market, marketPosition, this.getOracleDataForMarket(market.marketIndex)));
400
+ getTotalPerpPositionValue(marginCategory, liquidationBuffer, includeOpenOrders) {
401
+ return this.getUserAccount().perpPositions.reduce((totalPerpValue, perpPosition) => {
402
+ const market = this.clearingHouse.getPerpMarketAccount(perpPosition.marketIndex);
403
+ if (perpPosition.lpShares.gt(numericConstants_1.ZERO)) {
404
+ // is an lp
405
+ // clone so we dont mutate the position
406
+ perpPosition = this.getClonedPosition(perpPosition);
407
+ // settle position
408
+ const [settledPosition, dustBaa, _] = this.getSettledLPPosition(market.marketIndex);
409
+ perpPosition.baseAssetAmount =
410
+ settledPosition.baseAssetAmount.add(dustBaa);
411
+ perpPosition.quoteAssetAmount = settledPosition.quoteAssetAmount;
412
+ // open orders
413
+ let openAsks;
414
+ if (market.amm.maxBaseAssetReserve > market.amm.baseAssetReserve) {
415
+ openAsks = market.amm.maxBaseAssetReserve
416
+ .sub(market.amm.baseAssetReserve)
417
+ .mul(perpPosition.lpShares)
418
+ .div(market.amm.sqrtK)
419
+ .mul(new _1.BN(-1));
420
+ }
421
+ else {
422
+ openAsks = numericConstants_1.ZERO;
423
+ }
424
+ let openBids;
425
+ if (market.amm.minBaseAssetReserve < market.amm.baseAssetReserve) {
426
+ openBids = market.amm.baseAssetReserve
427
+ .sub(market.amm.minBaseAssetReserve)
428
+ .mul(perpPosition.lpShares)
429
+ .div(market.amm.sqrtK);
430
+ }
431
+ else {
432
+ openBids = numericConstants_1.ZERO;
433
+ }
434
+ perpPosition.openAsks = perpPosition.openAsks.add(openAsks);
435
+ perpPosition.openBids = perpPosition.openBids.add(openBids);
436
+ }
437
+ let valuationPrice = this.getOracleDataForMarket(market.marketIndex).price;
438
+ if ((0, types_1.isVariant)(market.status, 'settlement')) {
439
+ valuationPrice = market.settlementPrice;
440
+ }
441
+ const baseAssetAmount = includeOpenOrders
442
+ ? (0, margin_1.calculateWorstCaseBaseAssetAmount)(perpPosition)
443
+ : perpPosition.baseAssetAmount;
444
+ let baseAssetValue = baseAssetAmount
445
+ .abs()
446
+ .mul(valuationPrice)
447
+ .div(numericConstants_1.AMM_TO_QUOTE_PRECISION_RATIO.mul(numericConstants_1.PRICE_PRECISION));
448
+ if (marginCategory) {
449
+ let marginRatio = new _1.BN((0, _1.calculateMarketMarginRatio)(market, baseAssetAmount.abs(), marginCategory));
450
+ if (marginCategory === 'Initial') {
451
+ marginRatio = _1.BN.max(marginRatio, new _1.BN(this.getUserAccount().customMarginRatio));
452
+ }
453
+ if (liquidationBuffer !== undefined) {
454
+ marginRatio = marginRatio.add(liquidationBuffer);
455
+ }
456
+ baseAssetValue = baseAssetValue
457
+ .mul(marginRatio)
458
+ .div(numericConstants_1.MARGIN_PRECISION);
459
+ }
460
+ return totalPerpValue.add(baseAssetValue);
239
461
  }, numericConstants_1.ZERO);
240
462
  }
241
463
  /**
242
- * calculates position value from closing 100%
464
+ * calculates position value in margin system
243
465
  * @returns : Precision QUOTE_PRECISION
244
466
  */
245
- getPositionValue(marketIndex, oraclePriceData) {
467
+ getPerpPositionValue(marketIndex, oraclePriceData) {
246
468
  const userPosition = this.getUserPosition(marketIndex) || this.getEmptyPosition(marketIndex);
247
- const market = this.clearingHouse.getMarketAccount(userPosition.marketIndex);
248
- return (0, _1.calculateBaseAssetValue)(market, userPosition, oraclePriceData);
469
+ const market = this.clearingHouse.getPerpMarketAccount(userPosition.marketIndex);
470
+ return (0, margin_1.calculateBaseAssetValueWithOracle)(market, userPosition, oraclePriceData);
249
471
  }
250
472
  getPositionSide(currentPosition) {
251
473
  if (currentPosition.baseAssetAmount.gt(numericConstants_1.ZERO)) {
@@ -259,16 +481,16 @@ class ClearingHouseUser {
259
481
  }
260
482
  }
261
483
  /**
262
- * calculates average exit price for closing 100% of position
263
- * @returns : Precision MARK_PRICE_PRECISION
484
+ * calculates average exit price (optionally for closing up to 100% of position)
485
+ * @returns : Precision PRICE_PRECISION
264
486
  */
265
- getPositionEstimatedExitPriceAndPnl(position, amountToClose) {
266
- const market = this.clearingHouse.getMarketAccount(position.marketIndex);
487
+ getPositionEstimatedExitPriceAndPnl(position, amountToClose, useAMMClose = false) {
488
+ const market = this.clearingHouse.getPerpMarketAccount(position.marketIndex);
267
489
  const entryPrice = (0, position_1.calculateEntryPrice)(position);
268
490
  const oraclePriceData = this.getOracleDataForMarket(position.marketIndex);
269
491
  if (amountToClose) {
270
492
  if (amountToClose.eq(numericConstants_1.ZERO)) {
271
- return [(0, _1.calculateMarkPrice)(market, oraclePriceData), numericConstants_1.ZERO];
493
+ return [(0, _1.calculateReservePrice)(market, oraclePriceData), numericConstants_1.ZERO];
272
494
  }
273
495
  position = {
274
496
  baseAssetAmount: amountToClose,
@@ -277,18 +499,24 @@ class ClearingHouseUser {
277
499
  quoteAssetAmount: position.quoteAssetAmount,
278
500
  };
279
501
  }
280
- const baseAssetValue = (0, _1.calculateBaseAssetValue)(market, position, oraclePriceData);
502
+ let baseAssetValue;
503
+ if (useAMMClose) {
504
+ baseAssetValue = (0, _1.calculateBaseAssetValue)(market, position, oraclePriceData);
505
+ }
506
+ else {
507
+ baseAssetValue = (0, margin_1.calculateBaseAssetValueWithOracle)(market, position, oraclePriceData);
508
+ }
281
509
  if (position.baseAssetAmount.eq(numericConstants_1.ZERO)) {
282
510
  return [numericConstants_1.ZERO, numericConstants_1.ZERO];
283
511
  }
284
512
  const exitPrice = baseAssetValue
285
513
  .mul(numericConstants_1.AMM_TO_QUOTE_PRECISION_RATIO)
286
- .mul(numericConstants_1.MARK_PRICE_PRECISION)
514
+ .mul(numericConstants_1.PRICE_PRECISION)
287
515
  .div(position.baseAssetAmount.abs());
288
516
  const pnlPerBase = exitPrice.sub(entryPrice);
289
517
  const pnl = pnlPerBase
290
518
  .mul(position.baseAssetAmount)
291
- .div(numericConstants_1.MARK_PRICE_PRECISION)
519
+ .div(numericConstants_1.PRICE_PRECISION)
292
520
  .div(numericConstants_1.AMM_TO_QUOTE_PRECISION_RATIO);
293
521
  return [exitPrice, pnl];
294
522
  }
@@ -296,13 +524,13 @@ class ClearingHouseUser {
296
524
  * calculates current user leverage across all positions
297
525
  * @returns : Precision TEN_THOUSAND
298
526
  */
299
- getLeverage() {
300
- const totalCollateral = this.getTotalCollateral();
301
- const totalPositionValue = this.getTotalPositionValue();
302
- if (totalPositionValue.eq(numericConstants_1.ZERO) && totalCollateral.eq(numericConstants_1.ZERO)) {
527
+ getLeverage(marginCategory) {
528
+ const totalLiabilityValue = this.getTotalPerpPositionValue(marginCategory, undefined, true).add(this.getSpotMarketLiabilityValue(undefined, marginCategory, undefined, true));
529
+ const totalAssetValue = this.getSpotMarketAssetValue(undefined, marginCategory, true).add(this.getUnrealizedPNL(true, undefined, marginCategory));
530
+ if (totalAssetValue.eq(numericConstants_1.ZERO) && totalLiabilityValue.eq(numericConstants_1.ZERO)) {
303
531
  return numericConstants_1.ZERO;
304
532
  }
305
- return totalPositionValue.mul(numericConstants_1.TEN_THOUSAND).div(totalCollateral);
533
+ return totalLiabilityValue.mul(numericConstants_1.TEN_THOUSAND).div(totalAssetValue);
306
534
  }
307
535
  /**
308
536
  * calculates max allowable leverage exceeding hitting requirement category
@@ -310,22 +538,11 @@ class ClearingHouseUser {
310
538
  * @returns : Precision TEN_THOUSAND
311
539
  */
312
540
  getMaxLeverage(marketIndex, category = 'Initial') {
313
- const market = this.clearingHouse.getMarketAccount(marketIndex);
314
- let marginRatioCategory;
315
- switch (category) {
316
- case 'Initial':
317
- marginRatioCategory = market.marginRatioInitial;
318
- break;
319
- case 'Maintenance':
320
- marginRatioCategory = market.marginRatioMaintenance;
321
- break;
322
- case 'Partial':
323
- marginRatioCategory = market.marginRatioPartial;
324
- break;
325
- default:
326
- marginRatioCategory = market.marginRatioInitial;
327
- break;
328
- }
541
+ const market = this.clearingHouse.getPerpMarketAccount(marketIndex);
542
+ const marginRatioCategory = (0, _1.calculateMarketMarginRatio)(market,
543
+ // worstCaseBaseAssetAmount.abs(),
544
+ numericConstants_1.ZERO, // todo
545
+ category);
329
546
  const maxLeverage = numericConstants_1.TEN_THOUSAND.mul(numericConstants_1.TEN_THOUSAND).div(new _1.BN(marginRatioCategory));
330
547
  return maxLeverage;
331
548
  }
@@ -333,30 +550,34 @@ class ClearingHouseUser {
333
550
  * calculates margin ratio: total collateral / |total position value|
334
551
  * @returns : Precision TEN_THOUSAND
335
552
  */
336
- getMarginRatio() {
337
- const totalPositionValue = this.getTotalPositionValue();
338
- if (totalPositionValue.eq(numericConstants_1.ZERO)) {
553
+ getMarginRatio(marginCategory) {
554
+ const totalLiabilityValue = this.getTotalPerpPositionValue(marginCategory, undefined, true).add(this.getSpotMarketLiabilityValue(undefined, marginCategory, undefined, true));
555
+ if (totalLiabilityValue.eq(numericConstants_1.ZERO)) {
339
556
  return numericConstants_1.BN_MAX;
340
557
  }
341
- return this.getTotalCollateral().mul(numericConstants_1.TEN_THOUSAND).div(totalPositionValue);
558
+ const totalAssetValue = this.getSpotMarketAssetValue(undefined, marginCategory, true).add(this.getUnrealizedPNL(true, undefined, marginCategory));
559
+ return totalAssetValue.mul(numericConstants_1.TEN_THOUSAND).div(totalLiabilityValue);
342
560
  }
343
561
  canBeLiquidated() {
344
562
  const totalCollateral = this.getTotalCollateral();
345
- const partialMaintenanceRequirement = this.getPartialMarginRequirement();
346
- const marginRatio = this.getMarginRatio();
347
- const canLiquidate = totalCollateral.lt(partialMaintenanceRequirement);
348
- return [canLiquidate, marginRatio];
563
+ // if user being liq'd, can continue to be liq'd until total collateral above the margin requirement plus buffer
564
+ let liquidationBuffer = undefined;
565
+ if (this.getUserAccount().beingLiquidated) {
566
+ liquidationBuffer = new _1.BN(this.clearingHouse.getStateAccount().liquidationMarginBufferRatio);
567
+ }
568
+ const maintenanceRequirement = this.getMaintenanceMarginRequirement(liquidationBuffer);
569
+ return totalCollateral.lt(maintenanceRequirement);
349
570
  }
350
571
  /**
351
572
  * Checks if any user position cumulative funding differs from respective market cumulative funding
352
573
  * @returns
353
574
  */
354
575
  needsToSettleFundingPayment() {
355
- for (const userPosition of this.getUserAccount().positions) {
576
+ for (const userPosition of this.getUserAccount().perpPositions) {
356
577
  if (userPosition.baseAssetAmount.eq(numericConstants_1.ZERO)) {
357
578
  continue;
358
579
  }
359
- const market = this.clearingHouse.getMarketAccount(userPosition.marketIndex);
580
+ const market = this.clearingHouse.getPerpMarketAccount(userPosition.marketIndex);
360
581
  if (market.amm.cumulativeFundingRateLong.eq(userPosition.lastCumulativeFundingRate) ||
361
582
  market.amm.cumulativeFundingRateShort.eq(userPosition.lastCumulativeFundingRate)) {
362
583
  continue;
@@ -367,12 +588,12 @@ class ClearingHouseUser {
367
588
  }
368
589
  /**
369
590
  * Calculate the liquidation price of a position, with optional parameter to calculate the liquidation price after a trade
370
- * @param marketPosition
591
+ * @param PerpPosition
371
592
  * @param positionBaseSizeChange // change in position size to calculate liquidation price for : Precision 10^13
372
593
  * @param partial
373
- * @returns Precision : MARK_PRICE_PRECISION
594
+ * @returns Precision : PRICE_PRECISION
374
595
  */
375
- liquidationPrice(marketPosition, positionBaseSizeChange = numericConstants_1.ZERO, partial = false) {
596
+ liquidationPrice(perpPosition, positionBaseSizeChange = numericConstants_1.ZERO) {
376
597
  // solves formula for example canBeLiquidated below
377
598
  /* example: assume BTC price is $40k (examine 10% up/down)
378
599
 
@@ -385,37 +606,40 @@ class ClearingHouseUser {
385
606
  3. (10k - 4k) / (100k - 4k) = 6k/96k => .0625 */
386
607
  const totalCollateral = this.getTotalCollateral();
387
608
  // calculate the total position value ignoring any value from the target market of the trade
388
- const totalPositionValueExcludingTargetMarket = this.getTotalPositionValueExcludingMarket(marketPosition.marketIndex);
389
- const currentMarketPosition = this.getUserPosition(marketPosition.marketIndex) ||
390
- this.getEmptyPosition(marketPosition.marketIndex);
391
- const currentMarketPositionBaseSize = currentMarketPosition.baseAssetAmount;
392
- const proposedBaseAssetAmount = currentMarketPositionBaseSize.add(positionBaseSizeChange);
609
+ const totalPositionValueExcludingTargetMarket = this.getTotalPerpPositionValueExcludingMarket(perpPosition.marketIndex);
610
+ const currentPerpPosition = this.getUserPosition(perpPosition.marketIndex) ||
611
+ this.getEmptyPosition(perpPosition.marketIndex);
612
+ const currentPerpPositionBaseSize = currentPerpPosition.baseAssetAmount;
613
+ const proposedBaseAssetAmount = currentPerpPositionBaseSize.add(positionBaseSizeChange);
393
614
  // calculate position for current market after trade
394
- const proposedMarketPosition = {
395
- marketIndex: marketPosition.marketIndex,
615
+ const proposedPerpPosition = {
616
+ marketIndex: perpPosition.marketIndex,
396
617
  baseAssetAmount: proposedBaseAssetAmount,
397
- lastCumulativeFundingRate: currentMarketPosition.lastCumulativeFundingRate,
618
+ remainderBaseAssetAmount: numericConstants_1.ZERO,
398
619
  quoteAssetAmount: new _1.BN(0),
620
+ lastCumulativeFundingRate: numericConstants_1.ZERO,
399
621
  quoteEntryAmount: new _1.BN(0),
400
- openOrders: new _1.BN(0),
401
- unsettledPnl: new _1.BN(0),
622
+ openOrders: 0,
402
623
  openBids: new _1.BN(0),
403
624
  openAsks: new _1.BN(0),
625
+ settledPnl: numericConstants_1.ZERO,
626
+ lpShares: numericConstants_1.ZERO,
627
+ lastFeePerLp: numericConstants_1.ZERO,
628
+ lastNetBaseAssetAmountPerLp: numericConstants_1.ZERO,
629
+ lastNetQuoteAssetAmountPerLp: numericConstants_1.ZERO,
404
630
  };
405
631
  if (proposedBaseAssetAmount.eq(numericConstants_1.ZERO))
406
632
  return new _1.BN(-1);
407
- const market = this.clearingHouse.getMarketAccount(proposedMarketPosition.marketIndex);
408
- const proposedMarketPositionValue = (0, _1.calculateBaseAssetValue)(market, proposedMarketPosition, this.getOracleDataForMarket(market.marketIndex));
633
+ const market = this.clearingHouse.getPerpMarketAccount(proposedPerpPosition.marketIndex);
634
+ const proposedPerpPositionValue = (0, margin_1.calculateBaseAssetValueWithOracle)(market, proposedPerpPosition, this.getOracleDataForMarket(market.marketIndex));
409
635
  // total position value after trade
410
- const totalPositionValueAfterTrade = totalPositionValueExcludingTargetMarket.add(proposedMarketPositionValue);
411
- const marginRequirementExcludingTargetMarket = this.getUserAccount().positions.reduce((totalMarginRequirement, position) => {
412
- if (!position.marketIndex.eq(marketPosition.marketIndex)) {
413
- const market = this.clearingHouse.getMarketAccount(position.marketIndex);
414
- const positionValue = (0, _1.calculateBaseAssetValue)(market, position, this.getOracleDataForMarket(market.marketIndex));
636
+ const totalPositionValueAfterTrade = totalPositionValueExcludingTargetMarket.add(proposedPerpPositionValue);
637
+ const marginRequirementExcludingTargetMarket = this.getUserAccount().perpPositions.reduce((totalMarginRequirement, position) => {
638
+ if (position.marketIndex !== perpPosition.marketIndex) {
639
+ const market = this.clearingHouse.getPerpMarketAccount(position.marketIndex);
640
+ const positionValue = (0, margin_1.calculateBaseAssetValueWithOracle)(market, position, this.getOracleDataForMarket(market.marketIndex));
415
641
  const marketMarginRequirement = positionValue
416
- .mul(partial
417
- ? new _1.BN(market.marginRatioPartial)
418
- : new _1.BN(market.marginRatioMaintenance))
642
+ .mul(new _1.BN((0, _1.calculateMarketMarginRatio)(market, position.baseAssetAmount.abs(), 'Maintenance')))
419
643
  .div(numericConstants_1.MARGIN_PRECISION);
420
644
  totalMarginRequirement = totalMarginRequirement.add(marketMarginRequirement);
421
645
  }
@@ -424,18 +648,14 @@ class ClearingHouseUser {
424
648
  const freeCollateralExcludingTargetMarket = totalCollateral.sub(marginRequirementExcludingTargetMarket);
425
649
  // if the position value after the trade is less than free collateral, there is no liq price
426
650
  if (totalPositionValueAfterTrade.lte(freeCollateralExcludingTargetMarket) &&
427
- proposedMarketPosition.baseAssetAmount.abs().gt(numericConstants_1.ZERO)) {
651
+ proposedPerpPosition.baseAssetAmount.abs().gt(numericConstants_1.ZERO)) {
428
652
  return new _1.BN(-1);
429
653
  }
430
- const marginRequirementAfterTrade = marginRequirementExcludingTargetMarket.add(proposedMarketPositionValue
431
- .mul(partial
432
- ? new _1.BN(market.marginRatioPartial)
433
- : new _1.BN(market.marginRatioMaintenance))
654
+ const marginRequirementAfterTrade = marginRequirementExcludingTargetMarket.add(proposedPerpPositionValue
655
+ .mul(new _1.BN((0, _1.calculateMarketMarginRatio)(market, proposedPerpPosition.baseAssetAmount.abs(), 'Maintenance')))
434
656
  .div(numericConstants_1.MARGIN_PRECISION));
435
657
  const freeCollateralAfterTrade = totalCollateral.sub(marginRequirementAfterTrade);
436
- const marketMaxLeverage = partial
437
- ? this.getMaxLeverage(proposedMarketPosition.marketIndex, 'Partial')
438
- : this.getMaxLeverage(proposedMarketPosition.marketIndex, 'Maintenance');
658
+ const marketMaxLeverage = this.getMaxLeverage(proposedPerpPosition.marketIndex, 'Maintenance');
439
659
  let priceDelta;
440
660
  if (proposedBaseAssetAmount.lt(numericConstants_1.ZERO)) {
441
661
  priceDelta = freeCollateralAfterTrade
@@ -455,13 +675,13 @@ class ClearingHouseUser {
455
675
  }
456
676
  let markPriceAfterTrade;
457
677
  if (positionBaseSizeChange.eq(numericConstants_1.ZERO)) {
458
- markPriceAfterTrade = (0, _1.calculateMarkPrice)(this.clearingHouse.getMarketAccount(marketPosition.marketIndex), this.getOracleDataForMarket(marketPosition.marketIndex));
678
+ markPriceAfterTrade = (0, _1.calculateReservePrice)(this.clearingHouse.getPerpMarketAccount(perpPosition.marketIndex), this.getOracleDataForMarket(perpPosition.marketIndex));
459
679
  }
460
680
  else {
461
681
  const direction = positionBaseSizeChange.gt(numericConstants_1.ZERO)
462
682
  ? _1.PositionDirection.LONG
463
683
  : _1.PositionDirection.SHORT;
464
- markPriceAfterTrade = (0, _1.calculateTradeSlippage)(direction, positionBaseSizeChange.abs(), this.clearingHouse.getMarketAccount(marketPosition.marketIndex), 'base', this.getOracleDataForMarket(marketPosition.marketIndex))[3]; // newPrice after swap
684
+ markPriceAfterTrade = (0, _1.calculateTradeSlippage)(direction, positionBaseSizeChange.abs(), this.clearingHouse.getPerpMarketAccount(perpPosition.marketIndex), 'base', this.getOracleDataForMarket(perpPosition.marketIndex))[3]; // newPrice after swap
465
685
  }
466
686
  if (priceDelta.gt(markPriceAfterTrade)) {
467
687
  return new _1.BN(-1);
@@ -472,21 +692,21 @@ class ClearingHouseUser {
472
692
  * Calculates the estimated liquidation price for a position after closing a quote amount of the position.
473
693
  * @param positionMarketIndex
474
694
  * @param closeQuoteAmount
475
- * @returns : Precision MARK_PRICE_PRECISION
695
+ * @returns : Precision PRICE_PRECISION
476
696
  */
477
697
  liquidationPriceAfterClose(positionMarketIndex, closeQuoteAmount) {
478
698
  const currentPosition = this.getUserPosition(positionMarketIndex) ||
479
699
  this.getEmptyPosition(positionMarketIndex);
480
700
  const closeBaseAmount = currentPosition.baseAssetAmount
481
701
  .mul(closeQuoteAmount)
482
- .div(currentPosition.quoteAssetAmount)
702
+ .div(currentPosition.quoteAssetAmount.abs())
483
703
  .add(currentPosition.baseAssetAmount
484
704
  .mul(closeQuoteAmount)
485
- .mod(currentPosition.quoteAssetAmount))
705
+ .mod(currentPosition.quoteAssetAmount.abs()))
486
706
  .neg();
487
707
  return this.liquidationPrice({
488
708
  marketIndex: positionMarketIndex,
489
- }, closeBaseAmount, true);
709
+ }, closeBaseAmount);
490
710
  }
491
711
  /**
492
712
  * Get the maximum trade size for a given market, taking into account the user's current leverage, positions, collateral, etc.
@@ -523,7 +743,7 @@ class ClearingHouseUser {
523
743
  // 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.
524
744
  const oppositeSizeValueUSDC = targetingSameSide
525
745
  ? numericConstants_1.ZERO
526
- : this.getPositionValue(targetMarketIndex, oracleData);
746
+ : this.getPerpPositionValue(targetMarketIndex, oracleData);
527
747
  let maxPositionSize = this.getBuyingPower(targetMarketIndex);
528
748
  if (maxPositionSize.gte(numericConstants_1.ZERO)) {
529
749
  if (oppositeSizeValueUSDC.eq(numericConstants_1.ZERO)) {
@@ -538,23 +758,23 @@ class ClearingHouseUser {
538
758
  else {
539
759
  // current leverage is greater than max leverage - can only reduce position size
540
760
  if (!targetingSameSide) {
541
- const market = this.clearingHouse.getMarketAccount(targetMarketIndex);
542
- const marketPositionValue = this.getPositionValue(targetMarketIndex, oracleData);
761
+ const market = this.clearingHouse.getPerpMarketAccount(targetMarketIndex);
762
+ const perpPositionValue = this.getPerpPositionValue(targetMarketIndex, oracleData);
543
763
  const totalCollateral = this.getTotalCollateral();
544
764
  const marginRequirement = this.getInitialMarginRequirement();
545
- const marginFreedByClosing = marketPositionValue
765
+ const marginFreedByClosing = perpPositionValue
546
766
  .mul(new _1.BN(market.marginRatioInitial))
547
767
  .div(numericConstants_1.MARGIN_PRECISION);
548
768
  const marginRequirementAfterClosing = marginRequirement.sub(marginFreedByClosing);
549
769
  if (marginRequirementAfterClosing.gt(totalCollateral)) {
550
- maxPositionSize = marketPositionValue;
770
+ maxPositionSize = perpPositionValue;
551
771
  }
552
772
  else {
553
773
  const freeCollateralAfterClose = totalCollateral.sub(marginRequirementAfterClosing);
554
774
  const buyingPowerAfterClose = freeCollateralAfterClose
555
775
  .mul(this.getMaxLeverage(targetMarketIndex))
556
776
  .div(numericConstants_1.TEN_THOUSAND);
557
- maxPositionSize = marketPositionValue.add(buyingPowerAfterClose);
777
+ maxPositionSize = perpPositionValue.add(buyingPowerAfterClose);
558
778
  }
559
779
  }
560
780
  else {
@@ -578,7 +798,7 @@ class ClearingHouseUser {
578
798
  const currentPosition = this.getUserPosition(targetMarketIndex) ||
579
799
  this.getEmptyPosition(targetMarketIndex);
580
800
  const oracleData = this.getOracleDataForMarket(targetMarketIndex);
581
- let currentPositionQuoteAmount = this.getPositionValue(targetMarketIndex, oracleData);
801
+ let currentPositionQuoteAmount = this.getPerpPositionValue(targetMarketIndex, oracleData);
582
802
  const currentSide = currentPosition && currentPosition.baseAssetAmount.isNeg()
583
803
  ? _1.PositionDirection.SHORT
584
804
  : _1.PositionDirection.LONG;
@@ -586,13 +806,13 @@ class ClearingHouseUser {
586
806
  currentPositionQuoteAmount = currentPositionQuoteAmount.neg();
587
807
  if (tradeSide === _1.PositionDirection.SHORT)
588
808
  tradeQuoteAmount = tradeQuoteAmount.neg();
589
- const currentMarketPositionAfterTrade = currentPositionQuoteAmount
809
+ const currentPerpPositionAfterTrade = currentPositionQuoteAmount
590
810
  .add(tradeQuoteAmount)
591
811
  .abs();
592
- const totalPositionAfterTradeExcludingTargetMarket = this.getTotalPositionValueExcludingMarket(targetMarketIndex);
812
+ const totalPositionAfterTradeExcludingTargetMarket = this.getTotalPerpPositionValueExcludingMarket(targetMarketIndex);
593
813
  const totalCollateral = this.getTotalCollateral();
594
814
  if (totalCollateral.gt(numericConstants_1.ZERO)) {
595
- const newLeverage = currentMarketPositionAfterTrade
815
+ const newLeverage = currentPerpPositionAfterTrade
596
816
  .add(totalPositionAfterTradeExcludingTargetMarket)
597
817
  .abs()
598
818
  .mul(numericConstants_1.TEN_THOUSAND)
@@ -609,33 +829,33 @@ class ClearingHouseUser {
609
829
  * @returns feeForQuote : Precision QUOTE_PRECISION
610
830
  */
611
831
  calculateFeeForQuoteAmount(quoteAmount) {
612
- const feeStructure = this.clearingHouse.getStateAccount().feeStructure;
832
+ const feeTier = this.clearingHouse.getStateAccount().perpFeeStructure.feeTiers[0];
613
833
  return quoteAmount
614
- .mul(feeStructure.feeNumerator)
615
- .div(feeStructure.feeDenominator);
834
+ .mul(new _1.BN(feeTier.feeNumerator))
835
+ .div(new _1.BN(feeTier.feeDenominator));
616
836
  }
617
837
  /**
618
838
  * Get the total position value, excluding any position coming from the given target market
619
839
  * @param marketToIgnore
620
840
  * @returns positionValue : Precision QUOTE_PRECISION
621
841
  */
622
- getTotalPositionValueExcludingMarket(marketToIgnore) {
623
- const currentMarketPosition = this.getUserPosition(marketToIgnore) ||
842
+ getTotalPerpPositionValueExcludingMarket(marketToIgnore) {
843
+ const currentPerpPosition = this.getUserPosition(marketToIgnore) ||
624
844
  this.getEmptyPosition(marketToIgnore);
625
845
  const oracleData = this.getOracleDataForMarket(marketToIgnore);
626
- let currentMarketPositionValueUSDC = numericConstants_1.ZERO;
627
- if (currentMarketPosition) {
628
- currentMarketPositionValueUSDC = this.getPositionValue(marketToIgnore, oracleData);
846
+ let currentPerpPositionValueUSDC = numericConstants_1.ZERO;
847
+ if (currentPerpPosition) {
848
+ currentPerpPositionValueUSDC = this.getPerpPositionValue(marketToIgnore, oracleData);
629
849
  }
630
- return this.getTotalPositionValue().sub(currentMarketPositionValueUSDC);
850
+ return this.getTotalPerpPositionValue().sub(currentPerpPositionValueUSDC);
631
851
  }
632
852
  getOracleDataForMarket(marketIndex) {
633
- const oracleKey = this.clearingHouse.getMarketAccount(marketIndex).amm.oracle;
853
+ const oracleKey = this.clearingHouse.getPerpMarketAccount(marketIndex).amm.oracle;
634
854
  const oracleData = this.clearingHouse.getOraclePriceDataAndSlot(oracleKey).data;
635
855
  return oracleData;
636
856
  }
637
- getOracleDataForBank(bankIndex) {
638
- const oracleKey = this.clearingHouse.getBankAccount(bankIndex).oracle;
857
+ getOracleDataForSpotMarket(marketIndex) {
858
+ const oracleKey = this.clearingHouse.getSpotMarketAccount(marketIndex).oracle;
639
859
  const oracleData = this.clearingHouse.getOraclePriceDataAndSlot(oracleKey).data;
640
860
  return oracleData;
641
861
  }