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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (114) hide show
  1. package/lib/accounts/fetch.d.ts +2 -1
  2. package/lib/accounts/fetch.js +9 -1
  3. package/lib/accounts/pollingUserStatsAccountSubscriber.d.ts +27 -0
  4. package/lib/accounts/pollingUserStatsAccountSubscriber.js +113 -0
  5. package/lib/accounts/types.d.ts +14 -1
  6. package/lib/accounts/webSocketUserStatsAccountSubsriber.d.ts +20 -0
  7. package/lib/accounts/webSocketUserStatsAccountSubsriber.js +47 -0
  8. package/lib/addresses/pda.d.ts +1 -0
  9. package/lib/addresses/pda.js +8 -1
  10. package/lib/admin.d.ts +2 -0
  11. package/lib/admin.js +18 -0
  12. package/lib/clearingHouse.d.ts +24 -3
  13. package/lib/clearingHouse.js +352 -51
  14. package/lib/clearingHouseConfig.d.ts +1 -0
  15. package/lib/clearingHouseUser.d.ts +15 -15
  16. package/lib/clearingHouseUser.js +185 -73
  17. package/lib/clearingHouseUserStats.d.ts +17 -0
  18. package/lib/clearingHouseUserStats.js +36 -0
  19. package/lib/clearingHouseUserStatsConfig.d.ts +14 -0
  20. package/lib/clearingHouseUserStatsConfig.js +2 -0
  21. package/lib/config.js +1 -1
  22. package/lib/constants/banks.d.ts +2 -2
  23. package/lib/constants/banks.js +4 -3
  24. package/lib/constants/numericConstants.d.ts +2 -0
  25. package/lib/constants/numericConstants.js +3 -1
  26. package/lib/events/eventList.js +3 -0
  27. package/lib/events/types.d.ts +2 -1
  28. package/lib/factory/bigNum.d.ts +1 -0
  29. package/lib/factory/bigNum.js +37 -11
  30. package/lib/idl/clearing_house.json +692 -58
  31. package/lib/index.d.ts +1 -0
  32. package/lib/index.js +1 -0
  33. package/lib/math/amm.js +2 -2
  34. package/lib/math/bankBalance.d.ts +7 -1
  35. package/lib/math/bankBalance.js +76 -1
  36. package/lib/math/margin.d.ts +11 -0
  37. package/lib/math/margin.js +72 -0
  38. package/lib/math/market.d.ts +4 -1
  39. package/lib/math/market.js +35 -1
  40. package/lib/math/oracles.d.ts +3 -0
  41. package/lib/math/oracles.js +25 -5
  42. package/lib/math/position.d.ts +8 -0
  43. package/lib/math/position.js +43 -12
  44. package/lib/math/trade.js +2 -2
  45. package/lib/orders.d.ts +1 -2
  46. package/lib/orders.js +2 -77
  47. package/lib/tokenFaucet.d.ts +1 -0
  48. package/lib/tokenFaucet.js +23 -12
  49. package/lib/tx/retryTxSender.js +9 -2
  50. package/lib/types.d.ts +78 -10
  51. package/lib/types.js +12 -0
  52. package/lib/util/getTokenAddress.d.ts +2 -0
  53. package/lib/util/getTokenAddress.js +9 -0
  54. package/package.json +1 -1
  55. package/src/accounts/fetch.ts +27 -2
  56. package/src/accounts/pollingUserStatsAccountSubscriber.ts +172 -0
  57. package/src/accounts/types.ts +18 -0
  58. package/src/accounts/webSocketUserStatsAccountSubsriber.ts +80 -0
  59. package/src/addresses/pda.ts +13 -0
  60. package/src/admin.ts +29 -1
  61. package/src/clearingHouse.ts +619 -62
  62. package/src/clearingHouseConfig.ts +1 -0
  63. package/src/clearingHouseUser.ts +317 -105
  64. package/src/clearingHouseUserStats.ts +53 -0
  65. package/src/clearingHouseUserStatsConfig.ts +18 -0
  66. package/src/config.ts +1 -1
  67. package/src/constants/banks.js +42 -0
  68. package/src/constants/banks.ts +6 -3
  69. package/src/constants/markets.js +42 -0
  70. package/src/constants/numericConstants.js +41 -0
  71. package/src/constants/numericConstants.ts +3 -0
  72. package/src/events/eventList.ts +3 -0
  73. package/src/events/types.ts +2 -0
  74. package/src/factory/bigNum.js +37 -11
  75. package/src/factory/bigNum.ts +43 -13
  76. package/src/idl/clearing_house.json +692 -58
  77. package/src/index.ts +1 -0
  78. package/src/math/amm.ts +8 -5
  79. package/src/math/bankBalance.ts +147 -1
  80. package/src/math/margin.ts +124 -0
  81. package/src/math/market.ts +66 -1
  82. package/src/math/oracles.ts +42 -5
  83. package/src/math/position.ts +60 -9
  84. package/src/math/trade.ts +2 -2
  85. package/src/orders.ts +4 -157
  86. package/src/tokenFaucet.js +189 -0
  87. package/src/tokenFaucet.ts +38 -15
  88. package/src/tx/retryTxSender.ts +11 -3
  89. package/src/types.js +12 -1
  90. package/src/types.ts +83 -10
  91. package/src/{accounts/fetch.js → util/computeUnits.js} +11 -13
  92. package/src/util/getTokenAddress.js +9 -0
  93. package/src/util/getTokenAddress.ts +18 -0
  94. package/tests/bn/test.ts +2 -0
  95. package/src/accounts/bulkAccountLoader.js +0 -197
  96. package/src/accounts/bulkUserSubscription.js +0 -33
  97. package/src/accounts/pollingClearingHouseAccountSubscriber.js +0 -311
  98. package/src/accounts/pollingOracleSubscriber.js +0 -93
  99. package/src/accounts/pollingTokenAccountSubscriber.js +0 -90
  100. package/src/accounts/pollingUserAccountSubscriber.js +0 -132
  101. package/src/accounts/types.js +0 -10
  102. package/src/accounts/utils.js +0 -7
  103. package/src/accounts/webSocketAccountSubscriber.js +0 -93
  104. package/src/accounts/webSocketClearingHouseAccountSubscriber.js +0 -233
  105. package/src/accounts/webSocketUserAccountSubscriber.js +0 -62
  106. package/src/addresses/pda.js +0 -104
  107. package/src/index.js +0 -100
  108. package/src/math/bankBalance.js +0 -75
  109. package/src/math/market.js +0 -57
  110. package/src/math/orders.js +0 -110
  111. package/src/math/position.js +0 -140
  112. package/src/mockUSDCFaucet.js +0 -280
  113. package/src/orders.js +0 -134
  114. package/src/tx/retryTxSender.js +0 -188
@@ -18,6 +18,7 @@ export type ClearingHouseConfig = {
18
18
  bankIndexes?: BN[];
19
19
  oracleInfos?: OracleInfo[];
20
20
  env?: DriftEnv;
21
+ userStats?: boolean;
21
22
  };
22
23
 
23
24
  type ClearingHouseSubscriptionConfig =
@@ -21,6 +21,7 @@ import {
21
21
  PRICE_TO_QUOTE_PRECISION,
22
22
  MARGIN_PRECISION,
23
23
  BANK_WEIGHT_PRECISION,
24
+ BANK_BALANCE_PRECISION_EXP,
24
25
  } from './constants/numericConstants';
25
26
  import {
26
27
  UserAccountSubscriber,
@@ -32,17 +33,26 @@ import {
32
33
  calculateBaseAssetValue,
33
34
  calculatePositionFundingPNL,
34
35
  calculatePositionPNL,
36
+ calculateUnsettledAssetWeight,
37
+ calculateMarketMarginRatio,
35
38
  PositionDirection,
36
39
  calculateTradeSlippage,
37
40
  BN,
38
41
  BankAccount,
39
42
  } from '.';
40
- import { getTokenAmount } from './math/bankBalance';
43
+ import {
44
+ getTokenAmount,
45
+ calculateAssetWeight,
46
+ calculateLiabilityWeight,
47
+ } from './math/bankBalance';
48
+ import {
49
+ calculateMarginBaseAssetValue,
50
+ calculateWorstCaseBaseAssetAmount,
51
+ } from './math/margin';
41
52
  import { OraclePriceData } from './oracles/types';
42
53
  import { ClearingHouseUserConfig } from './clearingHouseUserConfig';
43
54
  import { PollingUserAccountSubscriber } from './accounts/pollingUserAccountSubscriber';
44
55
  import { WebSocketUserAccountSubscriber } from './accounts/webSocketUserAccountSubscriber';
45
-
46
56
  export class ClearingHouseUser {
47
57
  clearingHouse: ClearingHouse;
48
58
  userAccountPublicKey: PublicKey;
@@ -124,9 +134,13 @@ export class ClearingHouseUser {
124
134
  quoteAssetAmount: ZERO,
125
135
  quoteEntryAmount: ZERO,
126
136
  openOrders: ZERO,
127
- unsettledPnl: ZERO,
128
137
  openBids: ZERO,
129
138
  openAsks: ZERO,
139
+ realizedPnl: ZERO,
140
+ lpShares: ZERO,
141
+ lastFeePerLp: ZERO,
142
+ lastNetBaseAssetAmountPerLp: ZERO,
143
+ lastNetQuoteAssetAmountPerLp: ZERO,
130
144
  };
131
145
  }
132
146
 
@@ -162,6 +176,98 @@ export class ClearingHouseUser {
162
176
  return userAccountRPCResponse.value !== null;
163
177
  }
164
178
 
179
+ /**
180
+ * calculates the market position if the lp position was settled
181
+ * @returns : userPosition
182
+ */
183
+ public getSettledLPPosition(marketIndex: BN): [UserPosition, BN] {
184
+ const position = this.getUserPosition(marketIndex);
185
+ const market = this.clearingHouse.getMarketAccount(position.marketIndex);
186
+ const nShares = position.lpShares;
187
+
188
+ const deltaBaa = market.amm.marketPositionPerLp.baseAssetAmount
189
+ .sub(position.lastNetBaseAssetAmountPerLp)
190
+ .mul(nShares)
191
+ .div(AMM_RESERVE_PRECISION);
192
+ const deltaQaa = market.amm.marketPositionPerLp.quoteAssetAmount
193
+ .sub(position.lastNetQuoteAssetAmountPerLp)
194
+ .mul(nShares)
195
+ .div(AMM_RESERVE_PRECISION);
196
+
197
+ function sign(v: BN) {
198
+ const sign = { true: new BN(1), false: new BN(-1) }[
199
+ v.gte(ZERO).toString()
200
+ ];
201
+ return sign;
202
+ }
203
+
204
+ const remainder = deltaBaa
205
+ .abs()
206
+ .mod(market.amm.baseAssetAmountStepSize)
207
+ .mul(sign(deltaBaa));
208
+ const _standardizedBaa = deltaBaa.sub(remainder);
209
+
210
+ let remainderBaa;
211
+ if (_standardizedBaa.abs().gte(market.amm.baseAssetAmountStepSize)) {
212
+ remainderBaa = remainder;
213
+ } else {
214
+ remainderBaa = deltaBaa;
215
+ }
216
+ const standardizedBaa = deltaBaa.sub(remainderBaa);
217
+
218
+ const reaminderPerLP = remainderBaa.mul(AMM_RESERVE_PRECISION).div(nShares);
219
+
220
+ position.baseAssetAmount = position.baseAssetAmount.add(standardizedBaa);
221
+ position.quoteAssetAmount = position.quoteAssetAmount.add(deltaQaa);
222
+
223
+ position.lastNetBaseAssetAmountPerLp =
224
+ market.amm.marketPositionPerLp.baseAssetAmount.sub(reaminderPerLP);
225
+
226
+ let updateType;
227
+ if (position.baseAssetAmount.eq(ZERO)) {
228
+ updateType = 'open';
229
+ } else if (sign(position.baseAssetAmount).eq(sign(deltaBaa))) {
230
+ updateType = 'increase';
231
+ } else if (position.baseAssetAmount.abs().gt(deltaBaa.abs())) {
232
+ updateType = 'reduce';
233
+ } else if (position.baseAssetAmount.abs().eq(deltaBaa.abs())) {
234
+ updateType = 'close';
235
+ } else {
236
+ updateType = 'flip';
237
+ }
238
+
239
+ let newQuoteEntry;
240
+ let pnl;
241
+ if (updateType == 'open' || updateType == 'increase') {
242
+ newQuoteEntry = position.quoteEntryAmount.add(deltaQaa);
243
+ pnl = 0;
244
+ } else if (updateType == 'reduce' || updateType == 'close') {
245
+ newQuoteEntry = position.quoteEntryAmount.sub(
246
+ position.quoteEntryAmount
247
+ .mul(deltaBaa.abs())
248
+ .div(position.baseAssetAmount.abs())
249
+ );
250
+ pnl = position.quoteEntryAmount.sub(newQuoteEntry);
251
+ } else {
252
+ newQuoteEntry = deltaQaa.sub(
253
+ deltaQaa.mul(position.baseAssetAmount.abs()).div(deltaBaa.abs())
254
+ );
255
+ pnl = position.quoteEntryAmount.add(deltaQaa.sub(newQuoteEntry));
256
+ }
257
+ position.quoteEntryAmount = newQuoteEntry;
258
+
259
+ if (position.baseAssetAmount.gt(ZERO)) {
260
+ position.lastCumulativeFundingRate = market.amm.cumulativeFundingRateLong;
261
+ } else if (position.baseAssetAmount.lt(ZERO)) {
262
+ position.lastCumulativeFundingRate =
263
+ market.amm.cumulativeFundingRateShort;
264
+ } else {
265
+ position.lastCumulativeFundingRate = ZERO;
266
+ }
267
+
268
+ return [position, pnl];
269
+ }
270
+
165
271
  /**
166
272
  * calculates Buying Power = FC * MAX_LEVERAGE
167
273
  * @returns : Precision QUOTE_PRECISION
@@ -184,26 +290,43 @@ export class ClearingHouseUser {
184
290
  }
185
291
 
186
292
  public getInitialMarginRequirement(): BN {
187
- return this.getUserAccount()
188
- .positions.reduce((marginRequirement, marketPosition) => {
293
+ const postionMarginRequirement = this.getUserAccount().positions.reduce(
294
+ (marginRequirement, marketPosition) => {
189
295
  const market = this.clearingHouse.getMarketAccount(
190
296
  marketPosition.marketIndex
191
297
  );
192
- return marginRequirement.add(
193
- calculateBaseAssetValue(
298
+ const worstCaseBaseAssetAmount =
299
+ calculateWorstCaseBaseAssetAmount(marketPosition);
300
+
301
+ const worstCaseAssetValue = worstCaseBaseAssetAmount
302
+ .abs()
303
+ .mul(this.getOracleDataForMarket(market.marketIndex).price)
304
+ .div(AMM_TO_QUOTE_PRECISION_RATIO.mul(MARK_PRICE_PRECISION));
305
+
306
+ const marketMarginRatio = new BN(
307
+ calculateMarketMarginRatio(
194
308
  market,
195
- marketPosition,
196
- this.getOracleDataForMarket(market.marketIndex)
309
+ worstCaseBaseAssetAmount.abs(),
310
+ 'Initial'
197
311
  )
198
- .mul(new BN(market.marginRatioInitial))
199
- .div(MARGIN_PRECISION)
200
312
  );
201
- }, ZERO)
202
- .add(this.getTotalLiability());
313
+ return marginRequirement.add(
314
+ worstCaseAssetValue.mul(marketMarginRatio).div(MARGIN_PRECISION)
315
+ );
316
+ },
317
+ ZERO
318
+ );
319
+
320
+ const bankMarginRequirement = this.getBankLiabilityValue(
321
+ undefined,
322
+ 'Initial'
323
+ );
324
+
325
+ return bankMarginRequirement.add(postionMarginRequirement);
203
326
  }
204
327
 
205
328
  /**
206
- * @returns The partial margin requirement in USDC. : QUOTE_PRECISION
329
+ * @returns The maintenance margin requirement in USDC. : QUOTE_PRECISION
207
330
  */
208
331
  public getMaintenanceMarginRequirement(): BN {
209
332
  return this.getUserAccount()
@@ -211,24 +334,40 @@ export class ClearingHouseUser {
211
334
  const market = this.clearingHouse.getMarketAccount(
212
335
  marketPosition.marketIndex
213
336
  );
337
+ const worstCaseBaseAssetAmount =
338
+ calculateWorstCaseBaseAssetAmount(marketPosition);
339
+
340
+ const worstCaseAssetValue = worstCaseBaseAssetAmount
341
+ .abs()
342
+ .mul(this.getOracleDataForMarket(market.marketIndex).price)
343
+ .div(AMM_TO_QUOTE_PRECISION_RATIO.mul(MARK_PRICE_PRECISION));
344
+
214
345
  return marginRequirement.add(
215
- calculateBaseAssetValue(
216
- market,
217
- marketPosition,
218
- this.getOracleDataForMarket(market.marketIndex)
219
- )
220
- .mul(new BN(market.marginRatioMaintenance))
346
+ worstCaseAssetValue
347
+ .mul(
348
+ new BN(
349
+ calculateMarketMarginRatio(
350
+ market,
351
+ worstCaseBaseAssetAmount.abs(),
352
+ 'Maintenance'
353
+ )
354
+ )
355
+ )
221
356
  .div(MARGIN_PRECISION)
222
357
  );
223
358
  }, ZERO)
224
- .add(this.getTotalLiability());
359
+ .add(this.getBankLiabilityValue(undefined, 'Maintenance'));
225
360
  }
226
361
 
227
362
  /**
228
363
  * calculates unrealized position price pnl
229
364
  * @returns : Precision QUOTE_PRECISION
230
365
  */
231
- public getUnrealizedPNL(withFunding?: boolean, marketIndex?: BN): BN {
366
+ public getUnrealizedPNL(
367
+ withFunding?: boolean,
368
+ marketIndex?: BN,
369
+ withWeightMarginCategory?: MarginCategory
370
+ ): BN {
232
371
  return this.getUserAccount()
233
372
  .positions.filter((pos) =>
234
373
  marketIndex ? pos.marketIndex === marketIndex : true
@@ -237,28 +376,28 @@ export class ClearingHouseUser {
237
376
  const market = this.clearingHouse.getMarketAccount(
238
377
  marketPosition.marketIndex
239
378
  );
240
- return pnl.add(
241
- calculatePositionPNL(
242
- market,
243
- marketPosition,
244
- withFunding,
245
- this.getOracleDataForMarket(market.marketIndex)
246
- )
379
+ let pnl0 = calculatePositionPNL(
380
+ market,
381
+ marketPosition,
382
+ withFunding,
383
+ this.getOracleDataForMarket(market.marketIndex)
247
384
  );
248
- }, ZERO);
249
- }
250
385
 
251
- /**
252
- * calculates unrealized position price pnl
253
- * @returns : Precision QUOTE_PRECISION
254
- */
255
- public getUnsettledPNL(marketIndex?: BN): BN {
256
- return this.getUserAccount()
257
- .positions.filter((pos) =>
258
- marketIndex ? pos.marketIndex === marketIndex : true
259
- )
260
- .reduce((pnl, marketPosition) => {
261
- return pnl.add(marketPosition.unsettledPnl);
386
+ if (withWeightMarginCategory !== undefined) {
387
+ if (pnl0.gt(ZERO)) {
388
+ pnl0 = pnl0
389
+ .mul(
390
+ calculateUnsettledAssetWeight(
391
+ market,
392
+ pnl0,
393
+ withWeightMarginCategory
394
+ )
395
+ )
396
+ .div(new BN(BANK_WEIGHT_PRECISION));
397
+ }
398
+ }
399
+
400
+ return pnl.add(pnl0);
262
401
  }, ZERO);
263
402
  }
264
403
 
@@ -279,14 +418,18 @@ export class ClearingHouseUser {
279
418
  }, ZERO);
280
419
  }
281
420
 
282
- public getTotalLiability(): BN {
421
+ public getBankLiabilityValue(
422
+ bankIndex?: BN,
423
+ withWeightMarginCategory?: MarginCategory
424
+ ): BN {
283
425
  return this.getUserAccount().bankBalances.reduce(
284
- (totalAssetValue, bankBalance) => {
426
+ (totalLiabilityValue, bankBalance) => {
285
427
  if (
286
428
  bankBalance.balance.eq(ZERO) ||
287
- isVariant(bankBalance.balanceType, 'deposit')
429
+ isVariant(bankBalance.balanceType, 'deposit') ||
430
+ (bankIndex !== undefined && !bankBalance.bankIndex.eq(bankIndex))
288
431
  ) {
289
- return totalAssetValue;
432
+ return totalLiabilityValue;
290
433
  }
291
434
 
292
435
  // Todo this needs to account for whether it's based on initial or maintenance requirements
@@ -299,23 +442,42 @@ export class ClearingHouseUser {
299
442
  bankAccount,
300
443
  bankBalance.balanceType
301
444
  );
302
- return totalAssetValue.add(
303
- tokenAmount
304
- .mul(this.getOracleDataForBank(bankAccount.bankIndex).price)
305
- .mul(bankAccount.initialLiabilityWeight)
306
- .div(BANK_WEIGHT_PRECISION)
307
- .div(MARK_PRICE_PRECISION)
308
- );
445
+
446
+ let liabilityValue = tokenAmount
447
+ .mul(this.getOracleDataForBank(bankAccount.bankIndex).price)
448
+ .div(MARK_PRICE_PRECISION)
449
+ .div(
450
+ new BN(10).pow(
451
+ new BN(bankAccount.decimals).sub(BANK_BALANCE_PRECISION_EXP)
452
+ )
453
+ );
454
+
455
+ if (withWeightMarginCategory !== undefined) {
456
+ const weight = calculateLiabilityWeight(
457
+ tokenAmount,
458
+ bankAccount,
459
+ withWeightMarginCategory
460
+ );
461
+ liabilityValue = liabilityValue
462
+ .mul(weight)
463
+ .div(BANK_WEIGHT_PRECISION);
464
+ }
465
+
466
+ return totalLiabilityValue.add(liabilityValue);
309
467
  },
310
468
  ZERO
311
469
  );
312
470
  }
313
471
 
314
- public getCollateralValue(bankIndex?: BN): BN {
472
+ public getBankAssetValue(
473
+ bankIndex?: BN,
474
+ withWeightMarginCategory?: MarginCategory
475
+ ): BN {
315
476
  return this.getUserAccount().bankBalances.reduce(
316
477
  (totalAssetValue, bankBalance) => {
317
478
  if (
318
479
  bankBalance.balance.eq(ZERO) ||
480
+ isVariant(bankBalance.balanceType, 'borrow') ||
319
481
  (bankIndex !== undefined && !bankBalance.bankIndex.eq(bankIndex))
320
482
  ) {
321
483
  return totalAssetValue;
@@ -326,32 +488,46 @@ export class ClearingHouseUser {
326
488
  bankBalance.bankIndex
327
489
  );
328
490
 
329
- let tokenAmount = getTokenAmount(
491
+ const tokenAmount = getTokenAmount(
330
492
  bankBalance.balance,
331
493
  bankAccount,
332
494
  bankBalance.balanceType
333
495
  );
334
496
 
335
- if (isVariant(bankBalance.balanceType, 'borrow')) {
336
- tokenAmount = tokenAmount.mul(new BN(-1));
497
+ let assetValue = tokenAmount
498
+ .mul(this.getOracleDataForBank(bankAccount.bankIndex).price)
499
+ .div(MARK_PRICE_PRECISION)
500
+ .div(
501
+ new BN(10).pow(
502
+ new BN(bankAccount.decimals).sub(BANK_BALANCE_PRECISION_EXP)
503
+ )
504
+ );
505
+ if (withWeightMarginCategory !== undefined) {
506
+ const weight = calculateAssetWeight(
507
+ tokenAmount,
508
+ bankAccount,
509
+ withWeightMarginCategory
510
+ );
511
+ assetValue = assetValue.mul(weight).div(BANK_WEIGHT_PRECISION);
337
512
  }
338
513
 
339
- return totalAssetValue.add(
340
- tokenAmount
341
- .mul(this.getOracleDataForBank(bankAccount.bankIndex).price)
342
- .div(MARK_PRICE_PRECISION)
343
- );
514
+ return totalAssetValue.add(assetValue);
344
515
  },
345
516
  ZERO
346
517
  );
347
518
  }
348
519
 
520
+ public getNetBankValue(withWeightMarginCategory?: MarginCategory): BN {
521
+ return this.getBankAssetValue(undefined, withWeightMarginCategory).sub(
522
+ this.getBankLiabilityValue(undefined, withWeightMarginCategory)
523
+ );
524
+ }
525
+
349
526
  /**
350
527
  * calculates TotalCollateral: collateral + unrealized pnl
351
- * TODO: rename to total equity (for perpetuals swaps)
352
528
  * @returns : Precision QUOTE_PRECISION
353
529
  */
354
- public getTotalCollateral(): BN {
530
+ public getTotalCollateral(marginCategory: MarginCategory = 'Initial'): BN {
355
531
  return this.getUserAccount()
356
532
  .bankBalances.reduce((totalAssetValue, bankBalance) => {
357
533
  if (
@@ -371,20 +547,32 @@ export class ClearingHouseUser {
371
547
  bankAccount,
372
548
  bankBalance.balanceType
373
549
  );
550
+
551
+ const weight = calculateAssetWeight(
552
+ tokenAmount,
553
+ bankAccount,
554
+ marginCategory
555
+ );
556
+
374
557
  return totalAssetValue.add(
375
558
  tokenAmount
376
559
  .mul(this.getOracleDataForBank(bankAccount.bankIndex).price)
377
- .mul(bankAccount.initialAssetWeight)
560
+ .mul(weight)
378
561
  .div(BANK_WEIGHT_PRECISION)
379
562
  .div(MARK_PRICE_PRECISION)
563
+ // Adjust for decimals of bank account
564
+ .div(
565
+ new BN(10).pow(
566
+ new BN(bankAccount.decimals).sub(BANK_BALANCE_PRECISION_EXP)
567
+ )
568
+ )
380
569
  );
381
570
  }, ZERO)
382
- .add(this.getUnrealizedPNL(true))
383
- .add(this.getUnsettledPNL());
571
+ .add(this.getUnrealizedPNL(true, undefined, marginCategory));
384
572
  }
385
573
 
386
574
  /**
387
- * calculates sum of position value across all positions
575
+ * calculates sum of position value across all positions in margin system
388
576
  * @returns : Precision QUOTE_PRECISION
389
577
  */
390
578
  getTotalPositionValue(): BN {
@@ -393,20 +581,20 @@ export class ClearingHouseUser {
393
581
  const market = this.clearingHouse.getMarketAccount(
394
582
  marketPosition.marketIndex
395
583
  );
396
- return positionValue.add(
397
- calculateBaseAssetValue(
398
- market,
399
- marketPosition,
400
- this.getOracleDataForMarket(market.marketIndex)
401
- )
584
+ const posVal = calculateMarginBaseAssetValue(
585
+ market,
586
+ marketPosition,
587
+ this.getOracleDataForMarket(market.marketIndex)
402
588
  );
589
+
590
+ return positionValue.add(posVal);
403
591
  },
404
592
  ZERO
405
593
  );
406
594
  }
407
595
 
408
596
  /**
409
- * calculates position value from closing 100%
597
+ * calculates position value in margin system
410
598
  * @returns : Precision QUOTE_PRECISION
411
599
  */
412
600
  public getPositionValue(
@@ -418,7 +606,7 @@ export class ClearingHouseUser {
418
606
  const market = this.clearingHouse.getMarketAccount(
419
607
  userPosition.marketIndex
420
608
  );
421
- return calculateBaseAssetValue(market, userPosition, oraclePriceData);
609
+ return calculateMarginBaseAssetValue(market, userPosition, oraclePriceData);
422
610
  }
423
611
 
424
612
  public getPositionSide(
@@ -434,12 +622,13 @@ export class ClearingHouseUser {
434
622
  }
435
623
 
436
624
  /**
437
- * calculates average exit price for closing 100% of position
625
+ * calculates average exit price (optionally for closing up to 100% of position)
438
626
  * @returns : Precision MARK_PRICE_PRECISION
439
627
  */
440
628
  public getPositionEstimatedExitPriceAndPnl(
441
629
  position: UserPosition,
442
- amountToClose?: BN
630
+ amountToClose?: BN,
631
+ useAMMClose = false
443
632
  ): [BN, BN] {
444
633
  const market = this.clearingHouse.getMarketAccount(position.marketIndex);
445
634
 
@@ -459,11 +648,21 @@ export class ClearingHouseUser {
459
648
  } as UserPosition;
460
649
  }
461
650
 
462
- const baseAssetValue = calculateBaseAssetValue(
463
- market,
464
- position,
465
- oraclePriceData
466
- );
651
+ let baseAssetValue: BN;
652
+
653
+ if (useAMMClose) {
654
+ baseAssetValue = calculateBaseAssetValue(
655
+ market,
656
+ position,
657
+ oraclePriceData
658
+ );
659
+ } else {
660
+ baseAssetValue = calculateMarginBaseAssetValue(
661
+ market,
662
+ position,
663
+ oraclePriceData
664
+ );
665
+ }
467
666
  if (position.baseAssetAmount.eq(ZERO)) {
468
667
  return [ZERO, ZERO];
469
668
  }
@@ -505,19 +704,13 @@ export class ClearingHouseUser {
505
704
  category: MarginCategory = 'Initial'
506
705
  ): BN {
507
706
  const market = this.clearingHouse.getMarketAccount(marketIndex);
508
- let marginRatioCategory: number;
509
-
510
- switch (category) {
511
- case 'Initial':
512
- marginRatioCategory = market.marginRatioInitial;
513
- break;
514
- case 'Maintenance':
515
- marginRatioCategory = market.marginRatioMaintenance;
516
- break;
517
- default:
518
- marginRatioCategory = market.marginRatioInitial;
519
- break;
520
- }
707
+
708
+ const marginRatioCategory = calculateMarketMarginRatio(
709
+ market,
710
+ // worstCaseBaseAssetAmount.abs(),
711
+ ZERO, // todo
712
+ category
713
+ );
521
714
  const maxLeverage = TEN_THOUSAND.mul(TEN_THOUSAND).div(
522
715
  new BN(marginRatioCategory)
523
716
  );
@@ -619,14 +812,17 @@ export class ClearingHouseUser {
619
812
  const proposedMarketPosition: UserPosition = {
620
813
  marketIndex: marketPosition.marketIndex,
621
814
  baseAssetAmount: proposedBaseAssetAmount,
622
- lastCumulativeFundingRate:
623
- currentMarketPosition.lastCumulativeFundingRate,
624
815
  quoteAssetAmount: new BN(0),
816
+ lastCumulativeFundingRate: ZERO,
625
817
  quoteEntryAmount: new BN(0),
626
818
  openOrders: new BN(0),
627
- unsettledPnl: new BN(0),
628
819
  openBids: new BN(0),
629
820
  openAsks: new BN(0),
821
+ realizedPnl: ZERO,
822
+ lpShares: ZERO,
823
+ lastFeePerLp: ZERO,
824
+ lastNetBaseAssetAmountPerLp: ZERO,
825
+ lastNetQuoteAssetAmountPerLp: ZERO,
630
826
  };
631
827
 
632
828
  if (proposedBaseAssetAmount.eq(ZERO)) return new BN(-1);
@@ -635,7 +831,7 @@ export class ClearingHouseUser {
635
831
  proposedMarketPosition.marketIndex
636
832
  );
637
833
 
638
- const proposedMarketPositionValue = calculateBaseAssetValue(
834
+ const proposedMarketPositionValue = calculateMarginBaseAssetValue(
639
835
  market,
640
836
  proposedMarketPosition,
641
837
  this.getOracleDataForMarket(market.marketIndex)
@@ -652,13 +848,21 @@ export class ClearingHouseUser {
652
848
  const market = this.clearingHouse.getMarketAccount(
653
849
  position.marketIndex
654
850
  );
655
- const positionValue = calculateBaseAssetValue(
851
+ const positionValue = calculateMarginBaseAssetValue(
656
852
  market,
657
853
  position,
658
854
  this.getOracleDataForMarket(market.marketIndex)
659
855
  );
660
856
  const marketMarginRequirement = positionValue
661
- .mul(new BN(market.marginRatioMaintenance))
857
+ .mul(
858
+ new BN(
859
+ calculateMarketMarginRatio(
860
+ market,
861
+ position.baseAssetAmount.abs(),
862
+ 'Maintenance'
863
+ )
864
+ )
865
+ )
662
866
  .div(MARGIN_PRECISION);
663
867
  totalMarginRequirement = totalMarginRequirement.add(
664
868
  marketMarginRequirement
@@ -684,7 +888,15 @@ export class ClearingHouseUser {
684
888
  const marginRequirementAfterTrade =
685
889
  marginRequirementExcludingTargetMarket.add(
686
890
  proposedMarketPositionValue
687
- .mul(new BN(market.marginRatioMaintenance))
891
+ .mul(
892
+ new BN(
893
+ calculateMarketMarginRatio(
894
+ market,
895
+ proposedMarketPosition.baseAssetAmount.abs(),
896
+ 'Maintenance'
897
+ )
898
+ )
899
+ )
688
900
  .div(MARGIN_PRECISION)
689
901
  );
690
902
  const freeCollateralAfterTrade = totalCollateral.sub(
@@ -755,11 +967,11 @@ export class ClearingHouseUser {
755
967
 
756
968
  const closeBaseAmount = currentPosition.baseAssetAmount
757
969
  .mul(closeQuoteAmount)
758
- .div(currentPosition.quoteAssetAmount)
970
+ .div(currentPosition.quoteAssetAmount.abs())
759
971
  .add(
760
972
  currentPosition.baseAssetAmount
761
973
  .mul(closeQuoteAmount)
762
- .mod(currentPosition.quoteAssetAmount)
974
+ .mod(currentPosition.quoteAssetAmount.abs())
763
975
  )
764
976
  .neg();
765
977