@drift-labs/sdk 0.2.0-master.26 → 0.2.0-master.27
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.
- package/lib/accounts/pollingClearingHouseAccountSubscriber.d.ts +1 -0
- package/lib/accounts/pollingClearingHouseAccountSubscriber.js +3 -0
- package/lib/accounts/types.d.ts +1 -0
- package/lib/accounts/webSocketClearingHouseAccountSubscriber.d.ts +1 -0
- package/lib/accounts/webSocketClearingHouseAccountSubscriber.js +3 -0
- package/lib/admin.d.ts +5 -2
- package/lib/admin.js +30 -4
- package/lib/clearingHouse.d.ts +2 -0
- package/lib/clearingHouse.js +14 -1
- package/lib/clearingHouseUser.d.ts +11 -9
- package/lib/clearingHouseUser.js +182 -106
- package/lib/config.js +1 -1
- package/lib/dlob/DLOB.d.ts +73 -0
- package/lib/dlob/DLOB.js +557 -0
- package/lib/dlob/DLOBNode.d.ts +52 -0
- package/lib/dlob/DLOBNode.js +82 -0
- package/lib/dlob/NodeList.d.ts +26 -0
- package/lib/dlob/NodeList.js +138 -0
- package/lib/idl/clearing_house.json +122 -202
- package/lib/index.d.ts +5 -0
- package/lib/index.js +5 -0
- package/lib/math/market.d.ts +1 -1
- package/lib/math/market.js +1 -1
- package/lib/math/orders.d.ts +3 -3
- package/lib/math/orders.js +31 -16
- package/lib/math/spotBalance.d.ts +3 -0
- package/lib/math/spotBalance.js +18 -1
- package/lib/math/spotPosition.d.ts +5 -1
- package/lib/math/spotPosition.js +16 -1
- package/lib/types.d.ts +24 -23
- package/lib/types.js +9 -3
- package/lib/userMap/userMap.d.ts +25 -0
- package/lib/userMap/userMap.js +73 -0
- package/lib/userMap/userStatsMap.d.ts +19 -0
- package/lib/userMap/userStatsMap.js +68 -0
- package/package.json +6 -3
- package/src/accounts/pollingClearingHouseAccountSubscriber.ts +4 -0
- package/src/accounts/types.ts +1 -0
- package/src/accounts/webSocketClearingHouseAccountSubscriber.ts +6 -0
- package/src/addresses/marketAddresses.js +26 -0
- package/src/admin.ts +50 -8
- package/src/assert/assert.js +1 -1
- package/src/clearingHouse.ts +23 -1
- package/src/clearingHouseUser.ts +364 -165
- package/src/config.ts +1 -1
- package/src/constants/banks.js +42 -0
- package/src/constants/{perpMarkets.js → markets.js} +11 -11
- package/src/dlob/DLOB.ts +884 -0
- package/src/dlob/DLOBNode.ts +163 -0
- package/src/dlob/NodeList.ts +185 -0
- package/src/events/eventList.js +23 -66
- package/src/events/txEventCache.js +19 -22
- package/src/examples/makeTradeExample.js +157 -0
- package/src/factory/bigNum.js +180 -183
- package/src/factory/oracleClient.js +9 -9
- package/src/idl/clearing_house.json +122 -202
- package/src/index.ts +5 -0
- package/src/math/auction.js +10 -10
- package/src/math/conversion.js +3 -4
- package/src/math/funding.js +175 -223
- package/src/math/market.ts +1 -1
- package/src/math/orders.ts +29 -21
- package/src/math/repeg.js +40 -40
- package/src/math/spotBalance.ts +26 -0
- package/src/math/spotPosition.ts +42 -1
- package/src/math/trade.js +74 -81
- package/src/math/utils.js +7 -8
- package/src/math/utils.js.map +1 -0
- package/src/oracles/oracleClientCache.js +9 -10
- package/src/oracles/pythClient.js +17 -52
- package/src/oracles/quoteAssetOracleClient.js +13 -44
- package/src/oracles/switchboardClient.js +37 -69
- package/src/oracles/types.js +1 -1
- package/src/token/index.js +4 -4
- package/src/tx/types.js +1 -1
- package/src/tx/utils.js +6 -7
- package/src/types.ts +25 -23
- package/src/userMap/userMap.ts +100 -0
- package/src/userMap/userStatsMap.ts +110 -0
- package/src/userName.js +5 -5
- package/src/util/computeUnits.js +11 -46
- package/src/util/getTokenAddress.js +9 -0
- package/src/util/promiseTimeout.js +5 -5
- package/src/util/tps.js +12 -46
- package/src/wallet.js +18 -55
- package/tests/bn/test.ts +2 -3
- package/tests/dlob/helpers.ts +322 -0
- package/tests/dlob/test.ts +2865 -0
- package/my-script/.env +0 -7
- package/my-script/getUserStats.ts +0 -106
- package/my-script/multiConnections.ts +0 -119
- package/my-script/test-regex.ts +0 -11
- package/my-script/utils.ts +0 -52
- package/src/accounts/bulkAccountLoader.js +0 -249
- package/src/accounts/bulkUserStatsSubscription.js +0 -75
- package/src/accounts/bulkUserSubscription.js +0 -75
- package/src/accounts/fetch.js +0 -92
- package/src/accounts/pollingClearingHouseAccountSubscriber.js +0 -465
- package/src/accounts/pollingOracleSubscriber.js +0 -156
- package/src/accounts/pollingTokenAccountSubscriber.js +0 -141
- package/src/accounts/pollingUserAccountSubscriber.js +0 -208
- package/src/accounts/pollingUserStatsAccountSubscriber.js +0 -208
- package/src/accounts/types.js +0 -28
- package/src/accounts/utils.js +0 -7
- package/src/accounts/webSocketAccountSubscriber.js +0 -138
- package/src/accounts/webSocketClearingHouseAccountSubscriber.js +0 -433
- package/src/accounts/webSocketUserAccountSubscriber.js +0 -113
- package/src/accounts/webSocketUserStatsAccountSubsriber.js +0 -113
- package/src/addresses/pda.js +0 -186
- package/src/admin.js +0 -1284
- package/src/clearingHouse.js +0 -3433
- package/src/clearingHouseConfig.js +0 -2
- package/src/clearingHouseUser.js +0 -874
- package/src/clearingHouseUserConfig.js +0 -2
- package/src/clearingHouseUserStats.js +0 -115
- package/src/clearingHouseUserStatsConfig.js +0 -2
- package/src/config.js +0 -80
- package/src/constants/numericConstants.js +0 -48
- package/src/constants/spotMarkets.js +0 -51
- package/src/events/eventSubscriber.js +0 -202
- package/src/events/fetchLogs.js +0 -117
- package/src/events/pollingLogProvider.js +0 -113
- package/src/events/sort.js +0 -41
- package/src/events/types.js +0 -25
- package/src/events/webSocketLogProvider.js +0 -76
- package/src/index.js +0 -75
- package/src/math/amm.js +0 -422
- package/src/math/insurance.js +0 -27
- package/src/math/margin.js +0 -77
- package/src/math/market.js +0 -105
- package/src/math/oracles.js +0 -56
- package/src/math/orders.js +0 -153
- package/src/math/position.js +0 -172
- package/src/math/spotBalance.js +0 -176
- package/src/math/spotMarket.js +0 -8
- package/src/math/spotPosition.js +0 -8
- package/src/orderParams.js +0 -28
- package/src/serum/serumSubscriber.js +0 -102
- package/src/serum/types.js +0 -2
- package/src/slot/SlotSubscriber.js +0 -86
- package/src/tokenFaucet.js +0 -323
- package/src/tx/retryTxSender.js +0 -280
- package/src/types.js +0 -216
package/lib/clearingHouseUser.js
CHANGED
|
@@ -9,6 +9,7 @@ const spotBalance_1 = require("./math/spotBalance");
|
|
|
9
9
|
const margin_1 = require("./math/margin");
|
|
10
10
|
const pollingUserAccountSubscriber_1 = require("./accounts/pollingUserAccountSubscriber");
|
|
11
11
|
const webSocketUserAccountSubscriber_1 = require("./accounts/webSocketUserAccountSubscriber");
|
|
12
|
+
const spotPosition_1 = require("./math/spotPosition");
|
|
12
13
|
class ClearingHouseUser {
|
|
13
14
|
constructor(config) {
|
|
14
15
|
var _a;
|
|
@@ -212,62 +213,8 @@ class ClearingHouseUser {
|
|
|
212
213
|
/**
|
|
213
214
|
* @returns The margin requirement of a certain type (Initial or Maintenance) in USDC. : QUOTE_PRECISION
|
|
214
215
|
*/
|
|
215
|
-
getMarginRequirement(
|
|
216
|
-
return this.
|
|
217
|
-
.perpPositions.reduce((marginRequirement, perpPosition) => {
|
|
218
|
-
const market = this.clearingHouse.getPerpMarketAccount(perpPosition.marketIndex);
|
|
219
|
-
if (perpPosition.lpShares.gt(numericConstants_1.ZERO)) {
|
|
220
|
-
// is an lp
|
|
221
|
-
// clone so we dont mutate the position
|
|
222
|
-
perpPosition = this.getClonedPosition(perpPosition);
|
|
223
|
-
// settle position
|
|
224
|
-
const [settledPosition, dustBaa, _] = this.getSettledLPPosition(market.marketIndex);
|
|
225
|
-
perpPosition.baseAssetAmount =
|
|
226
|
-
settledPosition.baseAssetAmount.add(dustBaa);
|
|
227
|
-
perpPosition.quoteAssetAmount = settledPosition.quoteAssetAmount;
|
|
228
|
-
// open orders
|
|
229
|
-
let openAsks;
|
|
230
|
-
if (market.amm.maxBaseAssetReserve > market.amm.baseAssetReserve) {
|
|
231
|
-
openAsks = market.amm.maxBaseAssetReserve
|
|
232
|
-
.sub(market.amm.baseAssetReserve)
|
|
233
|
-
.mul(perpPosition.lpShares)
|
|
234
|
-
.div(market.amm.sqrtK)
|
|
235
|
-
.mul(new _1.BN(-1));
|
|
236
|
-
}
|
|
237
|
-
else {
|
|
238
|
-
openAsks = numericConstants_1.ZERO;
|
|
239
|
-
}
|
|
240
|
-
let openBids;
|
|
241
|
-
if (market.amm.minBaseAssetReserve < market.amm.baseAssetReserve) {
|
|
242
|
-
openBids = market.amm.baseAssetReserve
|
|
243
|
-
.sub(market.amm.minBaseAssetReserve)
|
|
244
|
-
.mul(perpPosition.lpShares)
|
|
245
|
-
.div(market.amm.sqrtK);
|
|
246
|
-
}
|
|
247
|
-
else {
|
|
248
|
-
openBids = numericConstants_1.ZERO;
|
|
249
|
-
}
|
|
250
|
-
perpPosition.openAsks = perpPosition.openAsks.add(openAsks);
|
|
251
|
-
perpPosition.openBids = perpPosition.openBids.add(openBids);
|
|
252
|
-
}
|
|
253
|
-
let valuationPrice = this.getOracleDataForMarket(market.marketIndex).price;
|
|
254
|
-
if ((0, types_1.isVariant)(market.status, 'settlement')) {
|
|
255
|
-
valuationPrice = market.settlementPrice;
|
|
256
|
-
}
|
|
257
|
-
const worstCaseBaseAssetAmount = (0, margin_1.calculateWorstCaseBaseAssetAmount)(perpPosition);
|
|
258
|
-
const worstCaseAssetValue = worstCaseBaseAssetAmount
|
|
259
|
-
.abs()
|
|
260
|
-
.mul(valuationPrice)
|
|
261
|
-
.div(numericConstants_1.AMM_TO_QUOTE_PRECISION_RATIO.mul(numericConstants_1.MARK_PRICE_PRECISION));
|
|
262
|
-
const positionMarginRequirement = worstCaseAssetValue
|
|
263
|
-
.mul(new _1.BN((0, _1.calculateMarketMarginRatio)(market, worstCaseBaseAssetAmount.abs(), type)))
|
|
264
|
-
.div(numericConstants_1.MARGIN_PRECISION);
|
|
265
|
-
if (liquidationBuffer !== undefined) {
|
|
266
|
-
positionMarginRequirement.add(worstCaseAssetValue.mul(liquidationBuffer).div(numericConstants_1.MARGIN_PRECISION));
|
|
267
|
-
}
|
|
268
|
-
return marginRequirement.add(positionMarginRequirement);
|
|
269
|
-
}, numericConstants_1.ZERO)
|
|
270
|
-
.add(this.getSpotMarketLiabilityValue(undefined, type, liquidationBuffer));
|
|
216
|
+
getMarginRequirement(marginCategory, liquidationBuffer) {
|
|
217
|
+
return this.getTotalPerpPositionValue(marginCategory, liquidationBuffer, true).add(this.getSpotMarketLiabilityValue(undefined, marginCategory, liquidationBuffer, true));
|
|
271
218
|
}
|
|
272
219
|
/**
|
|
273
220
|
* @returns The initial margin requirement in USDC. : QUOTE_PRECISION
|
|
@@ -315,55 +262,127 @@ class ClearingHouseUser {
|
|
|
315
262
|
return pnl.add((0, _1.calculatePositionFundingPNL)(market, perpPosition));
|
|
316
263
|
}, numericConstants_1.ZERO);
|
|
317
264
|
}
|
|
318
|
-
getSpotMarketLiabilityValue(marketIndex,
|
|
265
|
+
getSpotMarketLiabilityValue(marketIndex, marginCategory, liquidationBuffer, includeOpenOrders) {
|
|
319
266
|
return this.getUserAccount().spotPositions.reduce((totalLiabilityValue, spotPosition) => {
|
|
320
|
-
if (
|
|
321
|
-
(0, types_1.isVariant)(spotPosition.balanceType, 'deposit') ||
|
|
267
|
+
if ((0, spotPosition_1.isSpotPositionAvailable)(spotPosition) ||
|
|
322
268
|
(marketIndex !== undefined &&
|
|
323
269
|
!spotPosition.marketIndex.eq(marketIndex))) {
|
|
324
270
|
return totalLiabilityValue;
|
|
325
271
|
}
|
|
326
|
-
// Todo this needs to account for whether it's based on initial or maintenance requirements
|
|
327
272
|
const spotMarketAccount = this.clearingHouse.getSpotMarketAccount(spotPosition.marketIndex);
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
273
|
+
if (spotPosition.marketIndex.eq(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;
|
|
337
298
|
}
|
|
338
|
-
|
|
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
|
|
339
313
|
.mul(weight)
|
|
340
314
|
.div(numericConstants_1.SPOT_MARKET_WEIGHT_PRECISION);
|
|
315
|
+
newTotalLiabilityValue =
|
|
316
|
+
newTotalLiabilityValue.add(weightedTokenValue);
|
|
341
317
|
}
|
|
342
|
-
return
|
|
318
|
+
return newTotalLiabilityValue;
|
|
343
319
|
}, numericConstants_1.ZERO);
|
|
344
320
|
}
|
|
345
|
-
|
|
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) {
|
|
346
338
|
return this.getUserAccount().spotPositions.reduce((totalAssetValue, spotPosition) => {
|
|
347
|
-
if (
|
|
348
|
-
(0, types_1.isVariant)(spotPosition.balanceType, 'borrow') ||
|
|
339
|
+
if ((0, spotPosition_1.isSpotPositionAvailable)(spotPosition) ||
|
|
349
340
|
(marketIndex !== undefined &&
|
|
350
341
|
!spotPosition.marketIndex.eq(marketIndex))) {
|
|
351
342
|
return totalAssetValue;
|
|
352
343
|
}
|
|
353
344
|
// Todo this needs to account for whether it's based on initial or maintenance requirements
|
|
354
345
|
const spotMarketAccount = this.clearingHouse.getSpotMarketAccount(spotPosition.marketIndex);
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
346
|
+
if (spotPosition.marketIndex.eq(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
|
+
}
|
|
363
354
|
}
|
|
364
|
-
|
|
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
|
+
}
|
|
365
|
+
}
|
|
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;
|
|
365
376
|
}, numericConstants_1.ZERO);
|
|
366
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
|
+
}
|
|
367
386
|
getNetSpotMarketValue(withWeightMarginCategory) {
|
|
368
387
|
return this.getSpotMarketAssetValue(undefined, withWeightMarginCategory).sub(this.getSpotMarketLiabilityValue(undefined, withWeightMarginCategory));
|
|
369
388
|
}
|
|
@@ -372,24 +391,80 @@ class ClearingHouseUser {
|
|
|
372
391
|
* @returns : Precision QUOTE_PRECISION
|
|
373
392
|
*/
|
|
374
393
|
getTotalCollateral(marginCategory = 'Initial') {
|
|
375
|
-
return this.getSpotMarketAssetValue(undefined, marginCategory).add(this.getUnrealizedPNL(true, undefined, marginCategory));
|
|
394
|
+
return this.getSpotMarketAssetValue(undefined, marginCategory, true).add(this.getUnrealizedPNL(true, undefined, marginCategory));
|
|
376
395
|
}
|
|
377
396
|
/**
|
|
378
397
|
* calculates sum of position value across all positions in margin system
|
|
379
398
|
* @returns : Precision QUOTE_PRECISION
|
|
380
399
|
*/
|
|
381
|
-
|
|
382
|
-
return this.getUserAccount().perpPositions.reduce((
|
|
400
|
+
getTotalPerpPositionValue(marginCategory, liquidationBuffer, includeOpenOrders) {
|
|
401
|
+
return this.getUserAccount().perpPositions.reduce((totalPerpValue, perpPosition) => {
|
|
383
402
|
const market = this.clearingHouse.getPerpMarketAccount(perpPosition.marketIndex);
|
|
384
|
-
|
|
385
|
-
|
|
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.MARK_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);
|
|
386
461
|
}, numericConstants_1.ZERO);
|
|
387
462
|
}
|
|
388
463
|
/**
|
|
389
464
|
* calculates position value in margin system
|
|
390
465
|
* @returns : Precision QUOTE_PRECISION
|
|
391
466
|
*/
|
|
392
|
-
|
|
467
|
+
getPerpPositionValue(marketIndex, oraclePriceData) {
|
|
393
468
|
const userPosition = this.getUserPosition(marketIndex) || this.getEmptyPosition(marketIndex);
|
|
394
469
|
const market = this.clearingHouse.getPerpMarketAccount(userPosition.marketIndex);
|
|
395
470
|
return (0, margin_1.calculateBaseAssetValueWithOracle)(market, userPosition, oraclePriceData);
|
|
@@ -449,13 +524,13 @@ class ClearingHouseUser {
|
|
|
449
524
|
* calculates current user leverage across all positions
|
|
450
525
|
* @returns : Precision TEN_THOUSAND
|
|
451
526
|
*/
|
|
452
|
-
getLeverage() {
|
|
453
|
-
const
|
|
454
|
-
const
|
|
455
|
-
if (
|
|
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)) {
|
|
456
531
|
return numericConstants_1.ZERO;
|
|
457
532
|
}
|
|
458
|
-
return
|
|
533
|
+
return totalLiabilityValue.mul(numericConstants_1.TEN_THOUSAND).div(totalAssetValue);
|
|
459
534
|
}
|
|
460
535
|
/**
|
|
461
536
|
* calculates max allowable leverage exceeding hitting requirement category
|
|
@@ -475,12 +550,13 @@ class ClearingHouseUser {
|
|
|
475
550
|
* calculates margin ratio: total collateral / |total position value|
|
|
476
551
|
* @returns : Precision TEN_THOUSAND
|
|
477
552
|
*/
|
|
478
|
-
getMarginRatio() {
|
|
479
|
-
const
|
|
480
|
-
if (
|
|
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)) {
|
|
481
556
|
return numericConstants_1.BN_MAX;
|
|
482
557
|
}
|
|
483
|
-
|
|
558
|
+
const totalAssetValue = this.getSpotMarketAssetValue(undefined, marginCategory, true).add(this.getUnrealizedPNL(true, undefined, marginCategory));
|
|
559
|
+
return totalAssetValue.mul(numericConstants_1.TEN_THOUSAND).div(totalLiabilityValue);
|
|
484
560
|
}
|
|
485
561
|
canBeLiquidated() {
|
|
486
562
|
const totalCollateral = this.getTotalCollateral();
|
|
@@ -530,7 +606,7 @@ class ClearingHouseUser {
|
|
|
530
606
|
3. (10k - 4k) / (100k - 4k) = 6k/96k => .0625 */
|
|
531
607
|
const totalCollateral = this.getTotalCollateral();
|
|
532
608
|
// calculate the total position value ignoring any value from the target market of the trade
|
|
533
|
-
const totalPositionValueExcludingTargetMarket = this.
|
|
609
|
+
const totalPositionValueExcludingTargetMarket = this.getTotalPerpPositionValueExcludingMarket(perpPosition.marketIndex);
|
|
534
610
|
const currentPerpPosition = this.getUserPosition(perpPosition.marketIndex) ||
|
|
535
611
|
this.getEmptyPosition(perpPosition.marketIndex);
|
|
536
612
|
const currentPerpPositionBaseSize = currentPerpPosition.baseAssetAmount;
|
|
@@ -667,7 +743,7 @@ class ClearingHouseUser {
|
|
|
667
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.
|
|
668
744
|
const oppositeSizeValueUSDC = targetingSameSide
|
|
669
745
|
? numericConstants_1.ZERO
|
|
670
|
-
: this.
|
|
746
|
+
: this.getPerpPositionValue(targetMarketIndex, oracleData);
|
|
671
747
|
let maxPositionSize = this.getBuyingPower(targetMarketIndex);
|
|
672
748
|
if (maxPositionSize.gte(numericConstants_1.ZERO)) {
|
|
673
749
|
if (oppositeSizeValueUSDC.eq(numericConstants_1.ZERO)) {
|
|
@@ -683,7 +759,7 @@ class ClearingHouseUser {
|
|
|
683
759
|
// current leverage is greater than max leverage - can only reduce position size
|
|
684
760
|
if (!targetingSameSide) {
|
|
685
761
|
const market = this.clearingHouse.getPerpMarketAccount(targetMarketIndex);
|
|
686
|
-
const perpPositionValue = this.
|
|
762
|
+
const perpPositionValue = this.getPerpPositionValue(targetMarketIndex, oracleData);
|
|
687
763
|
const totalCollateral = this.getTotalCollateral();
|
|
688
764
|
const marginRequirement = this.getInitialMarginRequirement();
|
|
689
765
|
const marginFreedByClosing = perpPositionValue
|
|
@@ -722,7 +798,7 @@ class ClearingHouseUser {
|
|
|
722
798
|
const currentPosition = this.getUserPosition(targetMarketIndex) ||
|
|
723
799
|
this.getEmptyPosition(targetMarketIndex);
|
|
724
800
|
const oracleData = this.getOracleDataForMarket(targetMarketIndex);
|
|
725
|
-
let currentPositionQuoteAmount = this.
|
|
801
|
+
let currentPositionQuoteAmount = this.getPerpPositionValue(targetMarketIndex, oracleData);
|
|
726
802
|
const currentSide = currentPosition && currentPosition.baseAssetAmount.isNeg()
|
|
727
803
|
? _1.PositionDirection.SHORT
|
|
728
804
|
: _1.PositionDirection.LONG;
|
|
@@ -733,7 +809,7 @@ class ClearingHouseUser {
|
|
|
733
809
|
const currentPerpPositionAfterTrade = currentPositionQuoteAmount
|
|
734
810
|
.add(tradeQuoteAmount)
|
|
735
811
|
.abs();
|
|
736
|
-
const totalPositionAfterTradeExcludingTargetMarket = this.
|
|
812
|
+
const totalPositionAfterTradeExcludingTargetMarket = this.getTotalPerpPositionValueExcludingMarket(targetMarketIndex);
|
|
737
813
|
const totalCollateral = this.getTotalCollateral();
|
|
738
814
|
if (totalCollateral.gt(numericConstants_1.ZERO)) {
|
|
739
815
|
const newLeverage = currentPerpPositionAfterTrade
|
|
@@ -763,15 +839,15 @@ class ClearingHouseUser {
|
|
|
763
839
|
* @param marketToIgnore
|
|
764
840
|
* @returns positionValue : Precision QUOTE_PRECISION
|
|
765
841
|
*/
|
|
766
|
-
|
|
842
|
+
getTotalPerpPositionValueExcludingMarket(marketToIgnore) {
|
|
767
843
|
const currentPerpPosition = this.getUserPosition(marketToIgnore) ||
|
|
768
844
|
this.getEmptyPosition(marketToIgnore);
|
|
769
845
|
const oracleData = this.getOracleDataForMarket(marketToIgnore);
|
|
770
846
|
let currentPerpPositionValueUSDC = numericConstants_1.ZERO;
|
|
771
847
|
if (currentPerpPosition) {
|
|
772
|
-
currentPerpPositionValueUSDC = this.
|
|
848
|
+
currentPerpPositionValueUSDC = this.getPerpPositionValue(marketToIgnore, oracleData);
|
|
773
849
|
}
|
|
774
|
-
return this.
|
|
850
|
+
return this.getTotalPerpPositionValue().sub(currentPerpPositionValueUSDC);
|
|
775
851
|
}
|
|
776
852
|
getOracleDataForMarket(marketIndex) {
|
|
777
853
|
const oracleKey = this.clearingHouse.getPerpMarketAccount(marketIndex).amm.oracle;
|
package/lib/config.js
CHANGED
|
@@ -7,7 +7,7 @@ exports.configs = {
|
|
|
7
7
|
devnet: {
|
|
8
8
|
ENV: 'devnet',
|
|
9
9
|
PYTH_ORACLE_MAPPING_ADDRESS: 'BmA9Z6FjioHJPpjT39QazZyhDRUdZy2ezwx4GiDdE2u2',
|
|
10
|
-
CLEARING_HOUSE_PROGRAM_ID: '
|
|
10
|
+
CLEARING_HOUSE_PROGRAM_ID: 'CUowJP5Dea7pkc2mKrkX2RuZUrPXL3u419iBRXyZEWQz',
|
|
11
11
|
USDC_MINT_ADDRESS: '8zGuJQqwhZafTah7Uc7Z4tXRnguqkn5KLFAP8oV6PHe2',
|
|
12
12
|
PERP_MARKETS: perpMarkets_1.DevnetPerpMarkets,
|
|
13
13
|
SPOT_MARKETS: spotMarkets_1.DevnetSpotMarkets,
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/// <reference types="bn.js" />
|
|
2
|
+
import { NodeList } from './NodeList';
|
|
3
|
+
import { MarketType, BN, ClearingHouse, Order, SpotMarketAccount, PerpMarketAccount, OraclePriceData, SlotSubscriber, UserMap, MarketTypeStr } from '..';
|
|
4
|
+
import { PublicKey } from '@solana/web3.js';
|
|
5
|
+
import { DLOBNode, TriggerOrderNode } from '..';
|
|
6
|
+
export declare type MarketNodeLists = {
|
|
7
|
+
limit: {
|
|
8
|
+
ask: NodeList<'limit'>;
|
|
9
|
+
bid: NodeList<'limit'>;
|
|
10
|
+
};
|
|
11
|
+
floatingLimit: {
|
|
12
|
+
ask: NodeList<'floatingLimit'>;
|
|
13
|
+
bid: NodeList<'floatingLimit'>;
|
|
14
|
+
};
|
|
15
|
+
market: {
|
|
16
|
+
ask: NodeList<'market'>;
|
|
17
|
+
bid: NodeList<'market'>;
|
|
18
|
+
};
|
|
19
|
+
trigger: {
|
|
20
|
+
above: NodeList<'trigger'>;
|
|
21
|
+
below: NodeList<'trigger'>;
|
|
22
|
+
};
|
|
23
|
+
};
|
|
24
|
+
declare type OrderBookCallback = () => void;
|
|
25
|
+
export declare type NodeToFill = {
|
|
26
|
+
node: DLOBNode;
|
|
27
|
+
makerNode?: DLOBNode;
|
|
28
|
+
};
|
|
29
|
+
export declare type NodeToTrigger = {
|
|
30
|
+
node: TriggerOrderNode;
|
|
31
|
+
};
|
|
32
|
+
declare type Side = 'ask' | 'bid' | 'both';
|
|
33
|
+
export declare class DLOB {
|
|
34
|
+
openOrders: Map<MarketTypeStr, Set<string>>;
|
|
35
|
+
orderLists: Map<MarketTypeStr, Map<number, MarketNodeLists>>;
|
|
36
|
+
marketIndexToAccount: Map<MarketTypeStr, Map<number, PerpMarketAccount | SpotMarketAccount>>;
|
|
37
|
+
silent: boolean;
|
|
38
|
+
initialized: boolean;
|
|
39
|
+
/**
|
|
40
|
+
*
|
|
41
|
+
* @param perpMarkets The perp markets to maintain a DLOB for
|
|
42
|
+
* @param spotMarkets The spot markets to maintain a DLOB for
|
|
43
|
+
* @param silent set to true to prevent logging on inserts and removals
|
|
44
|
+
*/
|
|
45
|
+
constructor(perpMarkets: PerpMarketAccount[], spotMarkets: SpotMarketAccount[], silent?: boolean);
|
|
46
|
+
/**
|
|
47
|
+
* initializes a new DLOB instance
|
|
48
|
+
*
|
|
49
|
+
* @param clearingHouse The ClearingHouse instance to use for price data
|
|
50
|
+
* @returns a promise that resolves when the DLOB is initialized
|
|
51
|
+
*/
|
|
52
|
+
init(clearingHouse: ClearingHouse, userMap?: UserMap): Promise<boolean>;
|
|
53
|
+
insertOrder(order: Order, userAccount: PublicKey, onInsert?: OrderBookCallback): void;
|
|
54
|
+
trigger(order: Order, userAccount: PublicKey, onTrigger?: OrderBookCallback): void;
|
|
55
|
+
getListForOrder(order: Order): NodeList<any>;
|
|
56
|
+
findNodesToFill(marketIndex: BN, vBid: BN | undefined, vAsk: BN | undefined, slot: number, marketType: MarketType, oraclePriceData: OraclePriceData): NodeToFill[];
|
|
57
|
+
findCrossingNodesToFill(marketIndex: BN, vBid: BN | undefined, vAsk: BN | undefined, slot: number, marketType: MarketType, oraclePriceData: OraclePriceData): NodeToFill[];
|
|
58
|
+
findMarketNodesToFill(marketIndex: BN, slot: number, marketType: MarketType): NodeToFill[];
|
|
59
|
+
findJitAuctionNodesToFill(marketIndex: BN, slot: number, marketType: MarketType): NodeToFill[];
|
|
60
|
+
getMarketBids(marketIndex: BN, marketType: MarketType): Generator<DLOBNode>;
|
|
61
|
+
getMarketAsks(marketIndex: BN, marketType: MarketType): Generator<DLOBNode>;
|
|
62
|
+
getAsks(marketIndex: BN, vAsk: BN | undefined, slot: number, marketType: MarketType, oraclePriceData: OraclePriceData): Generator<DLOBNode>;
|
|
63
|
+
getBids(marketIndex: BN, vBid: BN | undefined, slot: number, marketType: MarketType, oraclePriceData: OraclePriceData): Generator<DLOBNode>;
|
|
64
|
+
findCrossingOrders(askNode: DLOBNode, bidNode: DLOBNode, oraclePriceData: OraclePriceData, slot: number): {
|
|
65
|
+
crossingNodes?: NodeToFill;
|
|
66
|
+
exhaustedSide?: Side;
|
|
67
|
+
};
|
|
68
|
+
getBestAsk(marketIndex: BN, vAsk: BN | undefined, slot: number, marketType: MarketType, oraclePriceData: OraclePriceData): BN;
|
|
69
|
+
getBestBid(marketIndex: BN, vBid: BN | undefined, slot: number, marketType: MarketType, oraclePriceData: OraclePriceData): BN;
|
|
70
|
+
findNodesToTrigger(marketIndex: BN, slot: number, oraclePrice: BN, marketType: MarketType): NodeToTrigger[];
|
|
71
|
+
printTopOfOrderLists(sdkConfig: any, clearingHouse: ClearingHouse, slotSubscriber: SlotSubscriber, marketIndex: BN, marketType: MarketType): void;
|
|
72
|
+
}
|
|
73
|
+
export {};
|