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

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