@drift-labs/sdk 0.2.0-master.25 → 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 +14 -13
- package/lib/accounts/pollingClearingHouseAccountSubscriber.js +30 -27
- package/lib/accounts/types.d.ts +9 -9
- package/lib/accounts/webSocketClearingHouseAccountSubscriber.d.ts +15 -14
- package/lib/accounts/webSocketClearingHouseAccountSubscriber.js +38 -34
- package/lib/addresses/pda.d.ts +7 -6
- package/lib/addresses/pda.js +31 -27
- package/lib/admin.d.ts +13 -7
- package/lib/admin.js +111 -44
- package/lib/clearingHouse.d.ts +71 -42
- package/lib/clearingHouse.js +767 -278
- package/lib/clearingHouseConfig.d.ts +2 -2
- package/lib/clearingHouseUser.d.ts +24 -22
- package/lib/clearingHouseUser.js +273 -177
- package/lib/config.d.ts +7 -7
- package/lib/config.js +21 -21
- package/lib/constants/numericConstants.d.ts +12 -12
- package/lib/constants/numericConstants.js +13 -13
- package/lib/constants/{markets.d.ts → perpMarkets.d.ts} +5 -5
- package/lib/constants/{markets.js → perpMarkets.js} +4 -4
- package/lib/constants/{banks.d.ts → spotMarkets.d.ts} +6 -6
- package/lib/constants/{banks.js → spotMarkets.js} +16 -16
- 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/events/types.d.ts +2 -1
- package/lib/events/types.js +1 -0
- package/lib/examples/makeTradeExample.js +7 -7
- package/lib/idl/clearing_house.json +1152 -503
- package/lib/index.d.ts +10 -3
- package/lib/index.js +10 -3
- package/lib/math/amm.d.ts +2 -2
- package/lib/math/amm.js +1 -1
- package/lib/math/funding.d.ts +6 -6
- package/lib/math/funding.js +2 -1
- package/lib/math/margin.d.ts +4 -4
- package/lib/math/margin.js +18 -11
- package/lib/math/market.d.ts +11 -10
- package/lib/math/market.js +30 -7
- package/lib/math/oracles.d.ts +2 -1
- package/lib/math/oracles.js +11 -1
- package/lib/math/orders.d.ts +6 -6
- package/lib/math/orders.js +31 -16
- package/lib/math/position.d.ts +13 -13
- package/lib/math/position.js +19 -19
- package/lib/math/spotBalance.d.ts +22 -0
- package/lib/math/spotBalance.js +193 -0
- package/lib/math/spotMarket.d.ts +4 -0
- package/lib/math/spotMarket.js +8 -0
- package/lib/math/spotPosition.d.ts +6 -0
- package/lib/math/spotPosition.js +23 -0
- package/lib/math/state.js +2 -2
- package/lib/math/trade.d.ts +4 -4
- package/lib/orderParams.d.ts +4 -4
- package/lib/orderParams.js +12 -4
- package/lib/serum/serumSubscriber.d.ts +23 -0
- package/lib/serum/serumSubscriber.js +41 -0
- package/lib/serum/types.d.ts +11 -0
- package/lib/serum/types.js +2 -0
- package/lib/tx/retryTxSender.d.ts +1 -1
- package/lib/tx/retryTxSender.js +4 -2
- package/lib/tx/types.d.ts +1 -1
- package/lib/types.d.ts +148 -57
- package/lib/types.js +39 -11
- 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 +42 -38
- package/src/accounts/types.ts +12 -9
- package/src/accounts/webSocketClearingHouseAccountSubscriber.ts +65 -52
- package/src/addresses/pda.ts +49 -44
- package/src/admin.ts +190 -55
- package/src/clearingHouse.ts +1092 -365
- package/src/clearingHouseConfig.ts +2 -2
- package/src/clearingHouseUser.ts +518 -255
- package/src/config.ts +30 -30
- package/src/constants/numericConstants.ts +17 -15
- package/src/constants/{markets.ts → perpMarkets.ts} +5 -5
- package/src/constants/{banks.ts → spotMarkets.ts} +19 -19
- package/src/dlob/DLOB.ts +884 -0
- package/src/dlob/DLOBNode.ts +163 -0
- package/src/dlob/NodeList.ts +185 -0
- package/src/events/types.ts +3 -0
- package/src/examples/makeTradeExample.js +152 -75
- package/src/examples/makeTradeExample.ts +10 -8
- package/src/idl/clearing_house.json +1152 -503
- package/src/index.ts +10 -3
- package/src/math/amm.ts +6 -3
- package/src/math/funding.ts +7 -7
- package/src/math/margin.ts +34 -23
- package/src/math/market.ts +72 -20
- package/src/math/oracles.ts +18 -1
- package/src/math/orders.ts +33 -25
- package/src/math/position.ts +31 -31
- package/src/math/spotBalance.ts +316 -0
- package/src/math/spotMarket.ts +9 -0
- package/src/math/spotPosition.ts +47 -0
- package/src/math/state.ts +2 -2
- package/src/math/trade.ts +4 -4
- package/src/orderParams.ts +16 -8
- package/src/serum/serumSubscriber.ts +80 -0
- package/src/serum/types.ts +13 -0
- package/src/tx/retryTxSender.ts +5 -2
- package/src/tx/types.ts +2 -1
- package/src/types.ts +135 -56
- package/src/userMap/userMap.ts +100 -0
- package/src/userMap/userStatsMap.ts +110 -0
- package/tests/bn/test.ts +2 -3
- package/tests/dlob/helpers.ts +322 -0
- package/tests/dlob/test.ts +2865 -0
- package/lib/math/bankBalance.d.ts +0 -15
- package/lib/math/bankBalance.js +0 -150
- package/src/constants/numericConstants.js +0 -41
- package/src/math/bankBalance.ts +0 -258
- package/src/math/oracles.js +0 -26
- package/src/math/state.js +0 -15
- package/src/orderParams.js +0 -20
- package/src/slot/SlotSubscriber.js +0 -39
- package/src/tokenFaucet.js +0 -189
package/lib/clearingHouseUser.js
CHANGED
|
@@ -5,10 +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
|
|
8
|
+
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;
|
|
@@ -59,11 +60,12 @@ class ClearingHouseUser {
|
|
|
59
60
|
* @returns userPosition
|
|
60
61
|
*/
|
|
61
62
|
getUserPosition(marketIndex) {
|
|
62
|
-
return this.getUserAccount().
|
|
63
|
+
return this.getUserAccount().perpPositions.find((position) => position.marketIndex.eq(marketIndex));
|
|
63
64
|
}
|
|
64
65
|
getEmptyPosition(marketIndex) {
|
|
65
66
|
return {
|
|
66
67
|
baseAssetAmount: numericConstants_1.ZERO,
|
|
68
|
+
remainderBaseAssetAmount: numericConstants_1.ZERO,
|
|
67
69
|
lastCumulativeFundingRate: numericConstants_1.ZERO,
|
|
68
70
|
marketIndex,
|
|
69
71
|
quoteAssetAmount: numericConstants_1.ZERO,
|
|
@@ -112,7 +114,7 @@ class ClearingHouseUser {
|
|
|
112
114
|
getSettledLPPosition(marketIndex) {
|
|
113
115
|
const _position = this.getUserPosition(marketIndex);
|
|
114
116
|
const position = this.getClonedPosition(_position);
|
|
115
|
-
const market = this.clearingHouse.
|
|
117
|
+
const market = this.clearingHouse.getPerpMarketAccount(position.marketIndex);
|
|
116
118
|
const nShares = position.lpShares;
|
|
117
119
|
const deltaBaa = market.amm.marketPositionPerLp.baseAssetAmount
|
|
118
120
|
.sub(position.lastNetBaseAssetAmountPerLp)
|
|
@@ -126,22 +128,22 @@ class ClearingHouseUser {
|
|
|
126
128
|
const sign = { true: new _1.BN(1), false: new _1.BN(-1) }[v.gte(numericConstants_1.ZERO).toString()];
|
|
127
129
|
return sign;
|
|
128
130
|
}
|
|
129
|
-
|
|
130
|
-
.abs()
|
|
131
|
-
.
|
|
132
|
-
|
|
133
|
-
const _standardizedBaa = deltaBaa.sub(remainder);
|
|
134
|
-
let remainderBaa;
|
|
135
|
-
if (_standardizedBaa.abs().gte(market.amm.baseAssetAmountStepSize)) {
|
|
136
|
-
remainderBaa = remainder;
|
|
131
|
+
function standardize(amount, stepsize) {
|
|
132
|
+
const remainder = amount.abs().mod(stepsize).mul(sign(amount));
|
|
133
|
+
const standardizedAmount = amount.sub(remainder);
|
|
134
|
+
return [standardizedAmount, remainder];
|
|
137
135
|
}
|
|
138
|
-
|
|
139
|
-
|
|
136
|
+
const [standardizedBaa, remainderBaa] = standardize(deltaBaa, market.amm.baseAssetAmountStepSize);
|
|
137
|
+
position.remainderBaseAssetAmount =
|
|
138
|
+
position.remainderBaseAssetAmount.add(remainderBaa);
|
|
139
|
+
if (position.remainderBaseAssetAmount
|
|
140
|
+
.abs()
|
|
141
|
+
.gte(market.amm.baseAssetAmountStepSize)) {
|
|
142
|
+
const [newStandardizedBaa, newRemainderBaa] = standardize(position.remainderBaseAssetAmount, market.amm.baseAssetAmountStepSize);
|
|
143
|
+
position.baseAssetAmount =
|
|
144
|
+
position.baseAssetAmount.add(newStandardizedBaa);
|
|
145
|
+
position.remainderBaseAssetAmount = newRemainderBaa;
|
|
140
146
|
}
|
|
141
|
-
const standardizedBaa = deltaBaa.sub(remainderBaa);
|
|
142
|
-
const reaminderPerLP = remainderBaa.mul(numericConstants_1.AMM_RESERVE_PRECISION).div(nShares);
|
|
143
|
-
position.lastNetBaseAssetAmountPerLp =
|
|
144
|
-
market.amm.marketPositionPerLp.baseAssetAmount.sub(reaminderPerLP);
|
|
145
147
|
let updateType;
|
|
146
148
|
if (position.baseAssetAmount.eq(numericConstants_1.ZERO)) {
|
|
147
149
|
updateType = 'open';
|
|
@@ -211,54 +213,8 @@ class ClearingHouseUser {
|
|
|
211
213
|
/**
|
|
212
214
|
* @returns The margin requirement of a certain type (Initial or Maintenance) in USDC. : QUOTE_PRECISION
|
|
213
215
|
*/
|
|
214
|
-
getMarginRequirement(
|
|
215
|
-
return this.
|
|
216
|
-
.positions.reduce((marginRequirement, marketPosition) => {
|
|
217
|
-
const market = this.clearingHouse.getMarketAccount(marketPosition.marketIndex);
|
|
218
|
-
if (marketPosition.lpShares.gt(numericConstants_1.ZERO)) {
|
|
219
|
-
// is an lp
|
|
220
|
-
// clone so we dont mutate the position
|
|
221
|
-
marketPosition = this.getClonedPosition(marketPosition);
|
|
222
|
-
// settle position
|
|
223
|
-
const [settledPosition, dustBaa, _] = this.getSettledLPPosition(market.marketIndex);
|
|
224
|
-
marketPosition.baseAssetAmount =
|
|
225
|
-
settledPosition.baseAssetAmount.add(dustBaa);
|
|
226
|
-
marketPosition.quoteAssetAmount = settledPosition.quoteAssetAmount;
|
|
227
|
-
// open orders
|
|
228
|
-
let openAsks;
|
|
229
|
-
if (market.amm.maxBaseAssetReserve > market.amm.baseAssetReserve) {
|
|
230
|
-
openAsks = market.amm.maxBaseAssetReserve
|
|
231
|
-
.sub(market.amm.baseAssetReserve)
|
|
232
|
-
.mul(marketPosition.lpShares)
|
|
233
|
-
.div(market.amm.sqrtK)
|
|
234
|
-
.mul(new _1.BN(-1));
|
|
235
|
-
}
|
|
236
|
-
else {
|
|
237
|
-
openAsks = numericConstants_1.ZERO;
|
|
238
|
-
}
|
|
239
|
-
let openBids;
|
|
240
|
-
if (market.amm.minBaseAssetReserve < market.amm.baseAssetReserve) {
|
|
241
|
-
openBids = market.amm.baseAssetReserve
|
|
242
|
-
.sub(market.amm.minBaseAssetReserve)
|
|
243
|
-
.mul(marketPosition.lpShares)
|
|
244
|
-
.div(market.amm.sqrtK);
|
|
245
|
-
}
|
|
246
|
-
else {
|
|
247
|
-
openBids = numericConstants_1.ZERO;
|
|
248
|
-
}
|
|
249
|
-
marketPosition.openAsks = marketPosition.openAsks.add(openAsks);
|
|
250
|
-
marketPosition.openBids = marketPosition.openBids.add(openBids);
|
|
251
|
-
}
|
|
252
|
-
const worstCaseBaseAssetAmount = (0, margin_1.calculateWorstCaseBaseAssetAmount)(marketPosition);
|
|
253
|
-
const worstCaseAssetValue = worstCaseBaseAssetAmount
|
|
254
|
-
.abs()
|
|
255
|
-
.mul(this.getOracleDataForMarket(market.marketIndex).price)
|
|
256
|
-
.div(numericConstants_1.AMM_TO_QUOTE_PRECISION_RATIO.mul(numericConstants_1.MARK_PRICE_PRECISION));
|
|
257
|
-
return marginRequirement.add(worstCaseAssetValue
|
|
258
|
-
.mul(new _1.BN((0, _1.calculateMarketMarginRatio)(market, worstCaseBaseAssetAmount.abs(), type)))
|
|
259
|
-
.div(numericConstants_1.MARGIN_PRECISION));
|
|
260
|
-
}, numericConstants_1.ZERO)
|
|
261
|
-
.add(this.getBankLiabilityValue(undefined, type));
|
|
216
|
+
getMarginRequirement(marginCategory, liquidationBuffer) {
|
|
217
|
+
return this.getTotalPerpPositionValue(marginCategory, liquidationBuffer, true).add(this.getSpotMarketLiabilityValue(undefined, marginCategory, liquidationBuffer, true));
|
|
262
218
|
}
|
|
263
219
|
/**
|
|
264
220
|
* @returns The initial margin requirement in USDC. : QUOTE_PRECISION
|
|
@@ -269,24 +225,26 @@ class ClearingHouseUser {
|
|
|
269
225
|
/**
|
|
270
226
|
* @returns The maintenance margin requirement in USDC. : QUOTE_PRECISION
|
|
271
227
|
*/
|
|
272
|
-
getMaintenanceMarginRequirement() {
|
|
273
|
-
return this.getMarginRequirement('Maintenance');
|
|
228
|
+
getMaintenanceMarginRequirement(liquidationBuffer) {
|
|
229
|
+
return this.getMarginRequirement('Maintenance', liquidationBuffer);
|
|
274
230
|
}
|
|
275
231
|
/**
|
|
276
232
|
* calculates unrealized position price pnl
|
|
277
233
|
* @returns : Precision QUOTE_PRECISION
|
|
278
234
|
*/
|
|
279
235
|
getUnrealizedPNL(withFunding, marketIndex, withWeightMarginCategory) {
|
|
236
|
+
const quoteSpotMarket = this.clearingHouse.getQuoteSpotMarketAccount();
|
|
280
237
|
return this.getUserAccount()
|
|
281
|
-
.
|
|
282
|
-
.reduce((unrealizedPnl,
|
|
283
|
-
const market = this.clearingHouse.
|
|
284
|
-
|
|
238
|
+
.perpPositions.filter((pos) => marketIndex ? pos.marketIndex === marketIndex : true)
|
|
239
|
+
.reduce((unrealizedPnl, perpPosition) => {
|
|
240
|
+
const market = this.clearingHouse.getPerpMarketAccount(perpPosition.marketIndex);
|
|
241
|
+
const oraclePriceData = this.getOracleDataForMarket(market.marketIndex);
|
|
242
|
+
let positionUnrealizedPnl = (0, _1.calculatePositionPNL)(market, perpPosition, withFunding, oraclePriceData);
|
|
285
243
|
if (withWeightMarginCategory !== undefined) {
|
|
286
244
|
if (positionUnrealizedPnl.gt(numericConstants_1.ZERO)) {
|
|
287
245
|
positionUnrealizedPnl = positionUnrealizedPnl
|
|
288
|
-
.mul((0, _1.calculateUnrealizedAssetWeight)(market, positionUnrealizedPnl, withWeightMarginCategory))
|
|
289
|
-
.div(new _1.BN(numericConstants_1.
|
|
246
|
+
.mul((0, _1.calculateUnrealizedAssetWeight)(market, quoteSpotMarket, positionUnrealizedPnl, withWeightMarginCategory, oraclePriceData))
|
|
247
|
+
.div(new _1.BN(numericConstants_1.SPOT_MARKET_WEIGHT_PRECISION));
|
|
290
248
|
}
|
|
291
249
|
}
|
|
292
250
|
return unrealizedPnl.add(positionUnrealizedPnl);
|
|
@@ -298,84 +256,217 @@ class ClearingHouseUser {
|
|
|
298
256
|
*/
|
|
299
257
|
getUnrealizedFundingPNL(marketIndex) {
|
|
300
258
|
return this.getUserAccount()
|
|
301
|
-
.
|
|
302
|
-
.reduce((pnl,
|
|
303
|
-
const market = this.clearingHouse.
|
|
304
|
-
return pnl.add((0, _1.calculatePositionFundingPNL)(market,
|
|
259
|
+
.perpPositions.filter((pos) => marketIndex ? pos.marketIndex === marketIndex : true)
|
|
260
|
+
.reduce((pnl, perpPosition) => {
|
|
261
|
+
const market = this.clearingHouse.getPerpMarketAccount(perpPosition.marketIndex);
|
|
262
|
+
return pnl.add((0, _1.calculatePositionFundingPNL)(market, perpPosition));
|
|
305
263
|
}, numericConstants_1.ZERO);
|
|
306
264
|
}
|
|
307
|
-
|
|
308
|
-
return this.getUserAccount().
|
|
309
|
-
if (
|
|
310
|
-
(
|
|
311
|
-
|
|
265
|
+
getSpotMarketLiabilityValue(marketIndex, marginCategory, liquidationBuffer, includeOpenOrders) {
|
|
266
|
+
return this.getUserAccount().spotPositions.reduce((totalLiabilityValue, spotPosition) => {
|
|
267
|
+
if ((0, spotPosition_1.isSpotPositionAvailable)(spotPosition) ||
|
|
268
|
+
(marketIndex !== undefined &&
|
|
269
|
+
!spotPosition.marketIndex.eq(marketIndex))) {
|
|
312
270
|
return totalLiabilityValue;
|
|
313
271
|
}
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
272
|
+
const spotMarketAccount = this.clearingHouse.getSpotMarketAccount(spotPosition.marketIndex);
|
|
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;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
const [worstCaseTokenAmount, worstCaseQuoteTokenAmount] = (0, spotPosition_1.getWorstCaseTokenAmounts)(spotPosition, spotMarketAccount, this.getOracleDataForSpotMarket(spotPosition.marketIndex));
|
|
301
|
+
let newTotalLiabilityValue = totalLiabilityValue;
|
|
302
|
+
if (worstCaseTokenAmount.lt(numericConstants_1.ZERO)) {
|
|
303
|
+
const baseLiabilityValue = this.getSpotLiabilityValue(worstCaseTokenAmount, oraclePriceData, spotMarketAccount, marginCategory, liquidationBuffer);
|
|
304
|
+
newTotalLiabilityValue =
|
|
305
|
+
newTotalLiabilityValue.add(baseLiabilityValue);
|
|
306
|
+
}
|
|
307
|
+
if (worstCaseQuoteTokenAmount.lt(numericConstants_1.ZERO)) {
|
|
308
|
+
let weight = numericConstants_1.SPOT_MARKET_WEIGHT_PRECISION;
|
|
309
|
+
if (marginCategory === 'Initial') {
|
|
310
|
+
weight = _1.BN.max(weight, new _1.BN(this.getUserAccount().customMarginRatio));
|
|
311
|
+
}
|
|
312
|
+
const weightedTokenValue = worstCaseQuoteTokenAmount
|
|
324
313
|
.mul(weight)
|
|
325
|
-
.div(numericConstants_1.
|
|
314
|
+
.div(numericConstants_1.SPOT_MARKET_WEIGHT_PRECISION);
|
|
315
|
+
newTotalLiabilityValue =
|
|
316
|
+
newTotalLiabilityValue.add(weightedTokenValue);
|
|
326
317
|
}
|
|
327
|
-
return
|
|
318
|
+
return newTotalLiabilityValue;
|
|
328
319
|
}, numericConstants_1.ZERO);
|
|
329
320
|
}
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
321
|
+
getSpotLiabilityValue(tokenAmount, oraclePriceData, spotMarketAccount, marginCategory, liquidationBuffer) {
|
|
322
|
+
let liabilityValue = (0, _1.getTokenValue)(tokenAmount, spotMarketAccount.decimals, oraclePriceData);
|
|
323
|
+
if (marginCategory !== undefined) {
|
|
324
|
+
let weight = (0, spotBalance_1.calculateLiabilityWeight)(tokenAmount, spotMarketAccount, marginCategory);
|
|
325
|
+
if (marginCategory === 'Initial') {
|
|
326
|
+
weight = _1.BN.max(weight, new _1.BN(this.getUserAccount().customMarginRatio));
|
|
327
|
+
}
|
|
328
|
+
if (liquidationBuffer !== undefined) {
|
|
329
|
+
weight = weight.add(liquidationBuffer);
|
|
330
|
+
}
|
|
331
|
+
liabilityValue = liabilityValue
|
|
332
|
+
.mul(weight)
|
|
333
|
+
.div(numericConstants_1.SPOT_MARKET_WEIGHT_PRECISION);
|
|
334
|
+
}
|
|
335
|
+
return liabilityValue;
|
|
336
|
+
}
|
|
337
|
+
getSpotMarketAssetValue(marketIndex, marginCategory, includeOpenOrders) {
|
|
338
|
+
return this.getUserAccount().spotPositions.reduce((totalAssetValue, spotPosition) => {
|
|
339
|
+
if ((0, spotPosition_1.isSpotPositionAvailable)(spotPosition) ||
|
|
340
|
+
(marketIndex !== undefined &&
|
|
341
|
+
!spotPosition.marketIndex.eq(marketIndex))) {
|
|
335
342
|
return totalAssetValue;
|
|
336
343
|
}
|
|
337
344
|
// Todo this needs to account for whether it's based on initial or maintenance requirements
|
|
338
|
-
const
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
345
|
+
const spotMarketAccount = this.clearingHouse.getSpotMarketAccount(spotPosition.marketIndex);
|
|
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
|
+
}
|
|
354
|
+
}
|
|
355
|
+
const oraclePriceData = this.getOracleDataForSpotMarket(spotPosition.marketIndex);
|
|
356
|
+
if (!includeOpenOrders) {
|
|
357
|
+
if ((0, types_1.isVariant)(spotPosition.balanceType, 'deposit')) {
|
|
358
|
+
const tokenAmount = (0, spotBalance_1.getTokenAmount)(spotPosition.balance, spotMarketAccount, spotPosition.balanceType);
|
|
359
|
+
const assetValue = this.getSpotAssetValue(tokenAmount, oraclePriceData, spotMarketAccount, marginCategory);
|
|
360
|
+
return totalAssetValue.add(assetValue);
|
|
361
|
+
}
|
|
362
|
+
else {
|
|
363
|
+
return totalAssetValue;
|
|
364
|
+
}
|
|
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);
|
|
347
371
|
}
|
|
348
|
-
|
|
372
|
+
if (worstCaseQuoteTokenAmount.gt(numericConstants_1.ZERO)) {
|
|
373
|
+
newTotalAssetValue = newTotalAssetValue.add(worstCaseQuoteTokenAmount);
|
|
374
|
+
}
|
|
375
|
+
return newTotalAssetValue;
|
|
349
376
|
}, numericConstants_1.ZERO);
|
|
350
377
|
}
|
|
351
|
-
|
|
352
|
-
|
|
378
|
+
getSpotAssetValue(tokenAmount, oraclePriceData, spotMarketAccount, marginCategory) {
|
|
379
|
+
let assetValue = (0, _1.getTokenValue)(tokenAmount, spotMarketAccount.decimals, oraclePriceData);
|
|
380
|
+
if (marginCategory !== undefined) {
|
|
381
|
+
const weight = (0, spotBalance_1.calculateAssetWeight)(tokenAmount, spotMarketAccount, marginCategory);
|
|
382
|
+
assetValue = assetValue.mul(weight).div(numericConstants_1.SPOT_MARKET_WEIGHT_PRECISION);
|
|
383
|
+
}
|
|
384
|
+
return assetValue;
|
|
385
|
+
}
|
|
386
|
+
getNetSpotMarketValue(withWeightMarginCategory) {
|
|
387
|
+
return this.getSpotMarketAssetValue(undefined, withWeightMarginCategory).sub(this.getSpotMarketLiabilityValue(undefined, withWeightMarginCategory));
|
|
353
388
|
}
|
|
354
389
|
/**
|
|
355
390
|
* calculates TotalCollateral: collateral + unrealized pnl
|
|
356
391
|
* @returns : Precision QUOTE_PRECISION
|
|
357
392
|
*/
|
|
358
393
|
getTotalCollateral(marginCategory = 'Initial') {
|
|
359
|
-
return this.
|
|
394
|
+
return this.getSpotMarketAssetValue(undefined, marginCategory, true).add(this.getUnrealizedPNL(true, undefined, marginCategory));
|
|
360
395
|
}
|
|
361
396
|
/**
|
|
362
397
|
* calculates sum of position value across all positions in margin system
|
|
363
398
|
* @returns : Precision QUOTE_PRECISION
|
|
364
399
|
*/
|
|
365
|
-
|
|
366
|
-
return this.getUserAccount().
|
|
367
|
-
const market = this.clearingHouse.
|
|
368
|
-
|
|
369
|
-
|
|
400
|
+
getTotalPerpPositionValue(marginCategory, liquidationBuffer, includeOpenOrders) {
|
|
401
|
+
return this.getUserAccount().perpPositions.reduce((totalPerpValue, perpPosition) => {
|
|
402
|
+
const market = this.clearingHouse.getPerpMarketAccount(perpPosition.marketIndex);
|
|
403
|
+
if (perpPosition.lpShares.gt(numericConstants_1.ZERO)) {
|
|
404
|
+
// is an lp
|
|
405
|
+
// clone so we dont mutate the position
|
|
406
|
+
perpPosition = this.getClonedPosition(perpPosition);
|
|
407
|
+
// settle position
|
|
408
|
+
const [settledPosition, dustBaa, _] = this.getSettledLPPosition(market.marketIndex);
|
|
409
|
+
perpPosition.baseAssetAmount =
|
|
410
|
+
settledPosition.baseAssetAmount.add(dustBaa);
|
|
411
|
+
perpPosition.quoteAssetAmount = settledPosition.quoteAssetAmount;
|
|
412
|
+
// open orders
|
|
413
|
+
let openAsks;
|
|
414
|
+
if (market.amm.maxBaseAssetReserve > market.amm.baseAssetReserve) {
|
|
415
|
+
openAsks = market.amm.maxBaseAssetReserve
|
|
416
|
+
.sub(market.amm.baseAssetReserve)
|
|
417
|
+
.mul(perpPosition.lpShares)
|
|
418
|
+
.div(market.amm.sqrtK)
|
|
419
|
+
.mul(new _1.BN(-1));
|
|
420
|
+
}
|
|
421
|
+
else {
|
|
422
|
+
openAsks = numericConstants_1.ZERO;
|
|
423
|
+
}
|
|
424
|
+
let openBids;
|
|
425
|
+
if (market.amm.minBaseAssetReserve < market.amm.baseAssetReserve) {
|
|
426
|
+
openBids = market.amm.baseAssetReserve
|
|
427
|
+
.sub(market.amm.minBaseAssetReserve)
|
|
428
|
+
.mul(perpPosition.lpShares)
|
|
429
|
+
.div(market.amm.sqrtK);
|
|
430
|
+
}
|
|
431
|
+
else {
|
|
432
|
+
openBids = numericConstants_1.ZERO;
|
|
433
|
+
}
|
|
434
|
+
perpPosition.openAsks = perpPosition.openAsks.add(openAsks);
|
|
435
|
+
perpPosition.openBids = perpPosition.openBids.add(openBids);
|
|
436
|
+
}
|
|
437
|
+
let valuationPrice = this.getOracleDataForMarket(market.marketIndex).price;
|
|
438
|
+
if ((0, types_1.isVariant)(market.status, 'settlement')) {
|
|
439
|
+
valuationPrice = market.settlementPrice;
|
|
440
|
+
}
|
|
441
|
+
const baseAssetAmount = includeOpenOrders
|
|
442
|
+
? (0, margin_1.calculateWorstCaseBaseAssetAmount)(perpPosition)
|
|
443
|
+
: perpPosition.baseAssetAmount;
|
|
444
|
+
let baseAssetValue = baseAssetAmount
|
|
445
|
+
.abs()
|
|
446
|
+
.mul(valuationPrice)
|
|
447
|
+
.div(numericConstants_1.AMM_TO_QUOTE_PRECISION_RATIO.mul(numericConstants_1.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);
|
|
370
461
|
}, numericConstants_1.ZERO);
|
|
371
462
|
}
|
|
372
463
|
/**
|
|
373
464
|
* calculates position value in margin system
|
|
374
465
|
* @returns : Precision QUOTE_PRECISION
|
|
375
466
|
*/
|
|
376
|
-
|
|
467
|
+
getPerpPositionValue(marketIndex, oraclePriceData) {
|
|
377
468
|
const userPosition = this.getUserPosition(marketIndex) || this.getEmptyPosition(marketIndex);
|
|
378
|
-
const market = this.clearingHouse.
|
|
469
|
+
const market = this.clearingHouse.getPerpMarketAccount(userPosition.marketIndex);
|
|
379
470
|
return (0, margin_1.calculateBaseAssetValueWithOracle)(market, userPosition, oraclePriceData);
|
|
380
471
|
}
|
|
381
472
|
getPositionSide(currentPosition) {
|
|
@@ -394,7 +485,7 @@ class ClearingHouseUser {
|
|
|
394
485
|
* @returns : Precision MARK_PRICE_PRECISION
|
|
395
486
|
*/
|
|
396
487
|
getPositionEstimatedExitPriceAndPnl(position, amountToClose, useAMMClose = false) {
|
|
397
|
-
const market = this.clearingHouse.
|
|
488
|
+
const market = this.clearingHouse.getPerpMarketAccount(position.marketIndex);
|
|
398
489
|
const entryPrice = (0, position_1.calculateEntryPrice)(position);
|
|
399
490
|
const oraclePriceData = this.getOracleDataForMarket(position.marketIndex);
|
|
400
491
|
if (amountToClose) {
|
|
@@ -433,13 +524,13 @@ class ClearingHouseUser {
|
|
|
433
524
|
* calculates current user leverage across all positions
|
|
434
525
|
* @returns : Precision TEN_THOUSAND
|
|
435
526
|
*/
|
|
436
|
-
getLeverage() {
|
|
437
|
-
const
|
|
438
|
-
const
|
|
439
|
-
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)) {
|
|
440
531
|
return numericConstants_1.ZERO;
|
|
441
532
|
}
|
|
442
|
-
return
|
|
533
|
+
return totalLiabilityValue.mul(numericConstants_1.TEN_THOUSAND).div(totalAssetValue);
|
|
443
534
|
}
|
|
444
535
|
/**
|
|
445
536
|
* calculates max allowable leverage exceeding hitting requirement category
|
|
@@ -447,7 +538,7 @@ class ClearingHouseUser {
|
|
|
447
538
|
* @returns : Precision TEN_THOUSAND
|
|
448
539
|
*/
|
|
449
540
|
getMaxLeverage(marketIndex, category = 'Initial') {
|
|
450
|
-
const market = this.clearingHouse.
|
|
541
|
+
const market = this.clearingHouse.getPerpMarketAccount(marketIndex);
|
|
451
542
|
const marginRatioCategory = (0, _1.calculateMarketMarginRatio)(market,
|
|
452
543
|
// worstCaseBaseAssetAmount.abs(),
|
|
453
544
|
numericConstants_1.ZERO, // todo
|
|
@@ -459,30 +550,34 @@ class ClearingHouseUser {
|
|
|
459
550
|
* calculates margin ratio: total collateral / |total position value|
|
|
460
551
|
* @returns : Precision TEN_THOUSAND
|
|
461
552
|
*/
|
|
462
|
-
getMarginRatio() {
|
|
463
|
-
const
|
|
464
|
-
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)) {
|
|
465
556
|
return numericConstants_1.BN_MAX;
|
|
466
557
|
}
|
|
467
|
-
|
|
558
|
+
const totalAssetValue = this.getSpotMarketAssetValue(undefined, marginCategory, true).add(this.getUnrealizedPNL(true, undefined, marginCategory));
|
|
559
|
+
return totalAssetValue.mul(numericConstants_1.TEN_THOUSAND).div(totalLiabilityValue);
|
|
468
560
|
}
|
|
469
561
|
canBeLiquidated() {
|
|
470
562
|
const totalCollateral = this.getTotalCollateral();
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
563
|
+
// if user being liq'd, can continue to be liq'd until total collateral above the margin requirement plus buffer
|
|
564
|
+
let liquidationBuffer = undefined;
|
|
565
|
+
if (this.getUserAccount().beingLiquidated) {
|
|
566
|
+
liquidationBuffer = new _1.BN(this.clearingHouse.getStateAccount().liquidationMarginBufferRatio);
|
|
567
|
+
}
|
|
568
|
+
const maintenanceRequirement = this.getMaintenanceMarginRequirement(liquidationBuffer);
|
|
569
|
+
return totalCollateral.lt(maintenanceRequirement);
|
|
475
570
|
}
|
|
476
571
|
/**
|
|
477
572
|
* Checks if any user position cumulative funding differs from respective market cumulative funding
|
|
478
573
|
* @returns
|
|
479
574
|
*/
|
|
480
575
|
needsToSettleFundingPayment() {
|
|
481
|
-
for (const userPosition of this.getUserAccount().
|
|
576
|
+
for (const userPosition of this.getUserAccount().perpPositions) {
|
|
482
577
|
if (userPosition.baseAssetAmount.eq(numericConstants_1.ZERO)) {
|
|
483
578
|
continue;
|
|
484
579
|
}
|
|
485
|
-
const market = this.clearingHouse.
|
|
580
|
+
const market = this.clearingHouse.getPerpMarketAccount(userPosition.marketIndex);
|
|
486
581
|
if (market.amm.cumulativeFundingRateLong.eq(userPosition.lastCumulativeFundingRate) ||
|
|
487
582
|
market.amm.cumulativeFundingRateShort.eq(userPosition.lastCumulativeFundingRate)) {
|
|
488
583
|
continue;
|
|
@@ -493,12 +588,12 @@ class ClearingHouseUser {
|
|
|
493
588
|
}
|
|
494
589
|
/**
|
|
495
590
|
* Calculate the liquidation price of a position, with optional parameter to calculate the liquidation price after a trade
|
|
496
|
-
* @param
|
|
591
|
+
* @param PerpPosition
|
|
497
592
|
* @param positionBaseSizeChange // change in position size to calculate liquidation price for : Precision 10^13
|
|
498
593
|
* @param partial
|
|
499
594
|
* @returns Precision : MARK_PRICE_PRECISION
|
|
500
595
|
*/
|
|
501
|
-
liquidationPrice(
|
|
596
|
+
liquidationPrice(perpPosition, positionBaseSizeChange = numericConstants_1.ZERO) {
|
|
502
597
|
// solves formula for example canBeLiquidated below
|
|
503
598
|
/* example: assume BTC price is $40k (examine 10% up/down)
|
|
504
599
|
|
|
@@ -511,15 +606,16 @@ class ClearingHouseUser {
|
|
|
511
606
|
3. (10k - 4k) / (100k - 4k) = 6k/96k => .0625 */
|
|
512
607
|
const totalCollateral = this.getTotalCollateral();
|
|
513
608
|
// calculate the total position value ignoring any value from the target market of the trade
|
|
514
|
-
const totalPositionValueExcludingTargetMarket = this.
|
|
515
|
-
const
|
|
516
|
-
this.getEmptyPosition(
|
|
517
|
-
const
|
|
518
|
-
const proposedBaseAssetAmount =
|
|
609
|
+
const totalPositionValueExcludingTargetMarket = this.getTotalPerpPositionValueExcludingMarket(perpPosition.marketIndex);
|
|
610
|
+
const currentPerpPosition = this.getUserPosition(perpPosition.marketIndex) ||
|
|
611
|
+
this.getEmptyPosition(perpPosition.marketIndex);
|
|
612
|
+
const currentPerpPositionBaseSize = currentPerpPosition.baseAssetAmount;
|
|
613
|
+
const proposedBaseAssetAmount = currentPerpPositionBaseSize.add(positionBaseSizeChange);
|
|
519
614
|
// calculate position for current market after trade
|
|
520
|
-
const
|
|
521
|
-
marketIndex:
|
|
615
|
+
const proposedPerpPosition = {
|
|
616
|
+
marketIndex: perpPosition.marketIndex,
|
|
522
617
|
baseAssetAmount: proposedBaseAssetAmount,
|
|
618
|
+
remainderBaseAssetAmount: numericConstants_1.ZERO,
|
|
523
619
|
quoteAssetAmount: new _1.BN(0),
|
|
524
620
|
lastCumulativeFundingRate: numericConstants_1.ZERO,
|
|
525
621
|
quoteEntryAmount: new _1.BN(0),
|
|
@@ -534,13 +630,13 @@ class ClearingHouseUser {
|
|
|
534
630
|
};
|
|
535
631
|
if (proposedBaseAssetAmount.eq(numericConstants_1.ZERO))
|
|
536
632
|
return new _1.BN(-1);
|
|
537
|
-
const market = this.clearingHouse.
|
|
538
|
-
const
|
|
633
|
+
const market = this.clearingHouse.getPerpMarketAccount(proposedPerpPosition.marketIndex);
|
|
634
|
+
const proposedPerpPositionValue = (0, margin_1.calculateBaseAssetValueWithOracle)(market, proposedPerpPosition, this.getOracleDataForMarket(market.marketIndex));
|
|
539
635
|
// total position value after trade
|
|
540
|
-
const totalPositionValueAfterTrade = totalPositionValueExcludingTargetMarket.add(
|
|
541
|
-
const marginRequirementExcludingTargetMarket = this.getUserAccount().
|
|
542
|
-
if (!position.marketIndex.eq(
|
|
543
|
-
const market = this.clearingHouse.
|
|
636
|
+
const totalPositionValueAfterTrade = totalPositionValueExcludingTargetMarket.add(proposedPerpPositionValue);
|
|
637
|
+
const marginRequirementExcludingTargetMarket = this.getUserAccount().perpPositions.reduce((totalMarginRequirement, position) => {
|
|
638
|
+
if (!position.marketIndex.eq(perpPosition.marketIndex)) {
|
|
639
|
+
const market = this.clearingHouse.getPerpMarketAccount(position.marketIndex);
|
|
544
640
|
const positionValue = (0, margin_1.calculateBaseAssetValueWithOracle)(market, position, this.getOracleDataForMarket(market.marketIndex));
|
|
545
641
|
const marketMarginRequirement = positionValue
|
|
546
642
|
.mul(new _1.BN((0, _1.calculateMarketMarginRatio)(market, position.baseAssetAmount.abs(), 'Maintenance')))
|
|
@@ -552,14 +648,14 @@ class ClearingHouseUser {
|
|
|
552
648
|
const freeCollateralExcludingTargetMarket = totalCollateral.sub(marginRequirementExcludingTargetMarket);
|
|
553
649
|
// if the position value after the trade is less than free collateral, there is no liq price
|
|
554
650
|
if (totalPositionValueAfterTrade.lte(freeCollateralExcludingTargetMarket) &&
|
|
555
|
-
|
|
651
|
+
proposedPerpPosition.baseAssetAmount.abs().gt(numericConstants_1.ZERO)) {
|
|
556
652
|
return new _1.BN(-1);
|
|
557
653
|
}
|
|
558
|
-
const marginRequirementAfterTrade = marginRequirementExcludingTargetMarket.add(
|
|
559
|
-
.mul(new _1.BN((0, _1.calculateMarketMarginRatio)(market,
|
|
654
|
+
const marginRequirementAfterTrade = marginRequirementExcludingTargetMarket.add(proposedPerpPositionValue
|
|
655
|
+
.mul(new _1.BN((0, _1.calculateMarketMarginRatio)(market, proposedPerpPosition.baseAssetAmount.abs(), 'Maintenance')))
|
|
560
656
|
.div(numericConstants_1.MARGIN_PRECISION));
|
|
561
657
|
const freeCollateralAfterTrade = totalCollateral.sub(marginRequirementAfterTrade);
|
|
562
|
-
const marketMaxLeverage = this.getMaxLeverage(
|
|
658
|
+
const marketMaxLeverage = this.getMaxLeverage(proposedPerpPosition.marketIndex, 'Maintenance');
|
|
563
659
|
let priceDelta;
|
|
564
660
|
if (proposedBaseAssetAmount.lt(numericConstants_1.ZERO)) {
|
|
565
661
|
priceDelta = freeCollateralAfterTrade
|
|
@@ -579,13 +675,13 @@ class ClearingHouseUser {
|
|
|
579
675
|
}
|
|
580
676
|
let markPriceAfterTrade;
|
|
581
677
|
if (positionBaseSizeChange.eq(numericConstants_1.ZERO)) {
|
|
582
|
-
markPriceAfterTrade = (0, _1.calculateMarkPrice)(this.clearingHouse.
|
|
678
|
+
markPriceAfterTrade = (0, _1.calculateMarkPrice)(this.clearingHouse.getPerpMarketAccount(perpPosition.marketIndex), this.getOracleDataForMarket(perpPosition.marketIndex));
|
|
583
679
|
}
|
|
584
680
|
else {
|
|
585
681
|
const direction = positionBaseSizeChange.gt(numericConstants_1.ZERO)
|
|
586
682
|
? _1.PositionDirection.LONG
|
|
587
683
|
: _1.PositionDirection.SHORT;
|
|
588
|
-
markPriceAfterTrade = (0, _1.calculateTradeSlippage)(direction, positionBaseSizeChange.abs(), this.clearingHouse.
|
|
684
|
+
markPriceAfterTrade = (0, _1.calculateTradeSlippage)(direction, positionBaseSizeChange.abs(), this.clearingHouse.getPerpMarketAccount(perpPosition.marketIndex), 'base', this.getOracleDataForMarket(perpPosition.marketIndex))[3]; // newPrice after swap
|
|
589
685
|
}
|
|
590
686
|
if (priceDelta.gt(markPriceAfterTrade)) {
|
|
591
687
|
return new _1.BN(-1);
|
|
@@ -647,7 +743,7 @@ class ClearingHouseUser {
|
|
|
647
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.
|
|
648
744
|
const oppositeSizeValueUSDC = targetingSameSide
|
|
649
745
|
? numericConstants_1.ZERO
|
|
650
|
-
: this.
|
|
746
|
+
: this.getPerpPositionValue(targetMarketIndex, oracleData);
|
|
651
747
|
let maxPositionSize = this.getBuyingPower(targetMarketIndex);
|
|
652
748
|
if (maxPositionSize.gte(numericConstants_1.ZERO)) {
|
|
653
749
|
if (oppositeSizeValueUSDC.eq(numericConstants_1.ZERO)) {
|
|
@@ -662,23 +758,23 @@ class ClearingHouseUser {
|
|
|
662
758
|
else {
|
|
663
759
|
// current leverage is greater than max leverage - can only reduce position size
|
|
664
760
|
if (!targetingSameSide) {
|
|
665
|
-
const market = this.clearingHouse.
|
|
666
|
-
const
|
|
761
|
+
const market = this.clearingHouse.getPerpMarketAccount(targetMarketIndex);
|
|
762
|
+
const perpPositionValue = this.getPerpPositionValue(targetMarketIndex, oracleData);
|
|
667
763
|
const totalCollateral = this.getTotalCollateral();
|
|
668
764
|
const marginRequirement = this.getInitialMarginRequirement();
|
|
669
|
-
const marginFreedByClosing =
|
|
765
|
+
const marginFreedByClosing = perpPositionValue
|
|
670
766
|
.mul(new _1.BN(market.marginRatioInitial))
|
|
671
767
|
.div(numericConstants_1.MARGIN_PRECISION);
|
|
672
768
|
const marginRequirementAfterClosing = marginRequirement.sub(marginFreedByClosing);
|
|
673
769
|
if (marginRequirementAfterClosing.gt(totalCollateral)) {
|
|
674
|
-
maxPositionSize =
|
|
770
|
+
maxPositionSize = perpPositionValue;
|
|
675
771
|
}
|
|
676
772
|
else {
|
|
677
773
|
const freeCollateralAfterClose = totalCollateral.sub(marginRequirementAfterClosing);
|
|
678
774
|
const buyingPowerAfterClose = freeCollateralAfterClose
|
|
679
775
|
.mul(this.getMaxLeverage(targetMarketIndex))
|
|
680
776
|
.div(numericConstants_1.TEN_THOUSAND);
|
|
681
|
-
maxPositionSize =
|
|
777
|
+
maxPositionSize = perpPositionValue.add(buyingPowerAfterClose);
|
|
682
778
|
}
|
|
683
779
|
}
|
|
684
780
|
else {
|
|
@@ -702,7 +798,7 @@ class ClearingHouseUser {
|
|
|
702
798
|
const currentPosition = this.getUserPosition(targetMarketIndex) ||
|
|
703
799
|
this.getEmptyPosition(targetMarketIndex);
|
|
704
800
|
const oracleData = this.getOracleDataForMarket(targetMarketIndex);
|
|
705
|
-
let currentPositionQuoteAmount = this.
|
|
801
|
+
let currentPositionQuoteAmount = this.getPerpPositionValue(targetMarketIndex, oracleData);
|
|
706
802
|
const currentSide = currentPosition && currentPosition.baseAssetAmount.isNeg()
|
|
707
803
|
? _1.PositionDirection.SHORT
|
|
708
804
|
: _1.PositionDirection.LONG;
|
|
@@ -710,13 +806,13 @@ class ClearingHouseUser {
|
|
|
710
806
|
currentPositionQuoteAmount = currentPositionQuoteAmount.neg();
|
|
711
807
|
if (tradeSide === _1.PositionDirection.SHORT)
|
|
712
808
|
tradeQuoteAmount = tradeQuoteAmount.neg();
|
|
713
|
-
const
|
|
809
|
+
const currentPerpPositionAfterTrade = currentPositionQuoteAmount
|
|
714
810
|
.add(tradeQuoteAmount)
|
|
715
811
|
.abs();
|
|
716
|
-
const totalPositionAfterTradeExcludingTargetMarket = this.
|
|
812
|
+
const totalPositionAfterTradeExcludingTargetMarket = this.getTotalPerpPositionValueExcludingMarket(targetMarketIndex);
|
|
717
813
|
const totalCollateral = this.getTotalCollateral();
|
|
718
814
|
if (totalCollateral.gt(numericConstants_1.ZERO)) {
|
|
719
|
-
const newLeverage =
|
|
815
|
+
const newLeverage = currentPerpPositionAfterTrade
|
|
720
816
|
.add(totalPositionAfterTradeExcludingTargetMarket)
|
|
721
817
|
.abs()
|
|
722
818
|
.mul(numericConstants_1.TEN_THOUSAND)
|
|
@@ -733,7 +829,7 @@ class ClearingHouseUser {
|
|
|
733
829
|
* @returns feeForQuote : Precision QUOTE_PRECISION
|
|
734
830
|
*/
|
|
735
831
|
calculateFeeForQuoteAmount(quoteAmount) {
|
|
736
|
-
const feeStructure = this.clearingHouse.getStateAccount().
|
|
832
|
+
const feeStructure = this.clearingHouse.getStateAccount().perpFeeStructure;
|
|
737
833
|
return quoteAmount
|
|
738
834
|
.mul(feeStructure.feeNumerator)
|
|
739
835
|
.div(feeStructure.feeDenominator);
|
|
@@ -743,23 +839,23 @@ class ClearingHouseUser {
|
|
|
743
839
|
* @param marketToIgnore
|
|
744
840
|
* @returns positionValue : Precision QUOTE_PRECISION
|
|
745
841
|
*/
|
|
746
|
-
|
|
747
|
-
const
|
|
842
|
+
getTotalPerpPositionValueExcludingMarket(marketToIgnore) {
|
|
843
|
+
const currentPerpPosition = this.getUserPosition(marketToIgnore) ||
|
|
748
844
|
this.getEmptyPosition(marketToIgnore);
|
|
749
845
|
const oracleData = this.getOracleDataForMarket(marketToIgnore);
|
|
750
|
-
let
|
|
751
|
-
if (
|
|
752
|
-
|
|
846
|
+
let currentPerpPositionValueUSDC = numericConstants_1.ZERO;
|
|
847
|
+
if (currentPerpPosition) {
|
|
848
|
+
currentPerpPositionValueUSDC = this.getPerpPositionValue(marketToIgnore, oracleData);
|
|
753
849
|
}
|
|
754
|
-
return this.
|
|
850
|
+
return this.getTotalPerpPositionValue().sub(currentPerpPositionValueUSDC);
|
|
755
851
|
}
|
|
756
852
|
getOracleDataForMarket(marketIndex) {
|
|
757
|
-
const oracleKey = this.clearingHouse.
|
|
853
|
+
const oracleKey = this.clearingHouse.getPerpMarketAccount(marketIndex).amm.oracle;
|
|
758
854
|
const oracleData = this.clearingHouse.getOraclePriceDataAndSlot(oracleKey).data;
|
|
759
855
|
return oracleData;
|
|
760
856
|
}
|
|
761
|
-
|
|
762
|
-
const oracleKey = this.clearingHouse.
|
|
857
|
+
getOracleDataForSpotMarket(marketIndex) {
|
|
858
|
+
const oracleKey = this.clearingHouse.getSpotMarketAccount(marketIndex).oracle;
|
|
763
859
|
const oracleData = this.clearingHouse.getOraclePriceDataAndSlot(oracleKey).data;
|
|
764
860
|
return oracleData;
|
|
765
861
|
}
|