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