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