@drift-labs/sdk 2.31.1-beta.22 → 2.31.1-beta.24
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/VERSION +1 -1
- package/lib/driftClient.d.ts +0 -1
- package/lib/driftClient.js +7 -11
- package/lib/idl/drift.json +1 -1
- package/lib/math/utils.d.ts +1 -0
- package/lib/math/utils.js +5 -1
- package/lib/orderParams.d.ts +14 -1
- package/lib/orderParams.js +17 -1
- package/lib/user.d.ts +45 -0
- package/lib/user.js +227 -4
- package/package.json +1 -1
- package/src/driftClient.ts +7 -16
- package/src/idl/drift.json +1 -1
- package/src/math/utils.ts +4 -0
- package/src/orderParams.ts +31 -1
- package/src/user.ts +454 -5
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
2.31.1-beta.
|
|
1
|
+
2.31.1-beta.24
|
package/lib/driftClient.d.ts
CHANGED
|
@@ -273,7 +273,6 @@ export declare class DriftClient {
|
|
|
273
273
|
signedFillTx: Transaction;
|
|
274
274
|
}>;
|
|
275
275
|
placePerpOrder(orderParams: OptionalOrderParams, txParams?: TxParams): Promise<TransactionSignature>;
|
|
276
|
-
getOrderParams(optionalOrderParams: OptionalOrderParams, marketType: MarketType): OrderParams;
|
|
277
276
|
getPlacePerpOrderIx(orderParams: OptionalOrderParams): Promise<TransactionInstruction>;
|
|
278
277
|
updateAMMs(marketIndexes: number[], txParams?: TxParams): Promise<TransactionSignature>;
|
|
279
278
|
getUpdateAMMsIx(marketIndexes: number[]): Promise<TransactionInstruction>;
|
package/lib/driftClient.js
CHANGED
|
@@ -53,6 +53,7 @@ const fetch_1 = require("./accounts/fetch");
|
|
|
53
53
|
const spotMarket_1 = require("./math/spotMarket");
|
|
54
54
|
const memcmp_1 = require("./memcmp");
|
|
55
55
|
const marinade_1 = require("./marinade");
|
|
56
|
+
const orderParams_1 = require("./orderParams");
|
|
56
57
|
/**
|
|
57
58
|
* # DriftClient
|
|
58
59
|
* This class is the main way to interact with Drift Protocol. It allows you to subscribe to the various accounts where the Market's state is stored, as well as: opening positions, liquidating, settling funding, depositing & withdrawing, and more.
|
|
@@ -1393,13 +1394,8 @@ class DriftClient {
|
|
|
1393
1394
|
this.perpMarketLastSlotCache.set(orderParams.marketIndex, slot);
|
|
1394
1395
|
return txSig;
|
|
1395
1396
|
}
|
|
1396
|
-
getOrderParams(optionalOrderParams, marketType) {
|
|
1397
|
-
return Object.assign({}, types_1.DefaultOrderParams, optionalOrderParams, {
|
|
1398
|
-
marketType,
|
|
1399
|
-
});
|
|
1400
|
-
}
|
|
1401
1397
|
async getPlacePerpOrderIx(orderParams) {
|
|
1402
|
-
orderParams =
|
|
1398
|
+
orderParams = (0, orderParams_1.getOrderParams)(orderParams, { marketType: types_1.MarketType.PERP });
|
|
1403
1399
|
const userAccountPublicKey = await this.getUserAccountPublicKey();
|
|
1404
1400
|
const remainingAccounts = this.getRemainingAccounts({
|
|
1405
1401
|
userAccounts: [this.getUserAccount()],
|
|
@@ -1699,7 +1695,7 @@ class DriftClient {
|
|
|
1699
1695
|
return txSig;
|
|
1700
1696
|
}
|
|
1701
1697
|
async getPlaceSpotOrderIx(orderParams) {
|
|
1702
|
-
orderParams =
|
|
1698
|
+
orderParams = (0, orderParams_1.getOrderParams)(orderParams, { marketType: types_1.MarketType.SPOT });
|
|
1703
1699
|
const userAccountPublicKey = await this.getUserAccountPublicKey();
|
|
1704
1700
|
const remainingAccounts = this.getRemainingAccounts({
|
|
1705
1701
|
userAccounts: [this.getUserAccount()],
|
|
@@ -2212,7 +2208,7 @@ class DriftClient {
|
|
|
2212
2208
|
return txSig;
|
|
2213
2209
|
}
|
|
2214
2210
|
async getPlaceAndTakePerpOrderIx(orderParams, makerInfo, referrerInfo) {
|
|
2215
|
-
orderParams =
|
|
2211
|
+
orderParams = (0, orderParams_1.getOrderParams)(orderParams, { marketType: types_1.MarketType.PERP });
|
|
2216
2212
|
const userStatsPublicKey = await this.getUserStatsAccountPublicKey();
|
|
2217
2213
|
const userAccountPublicKey = await this.getUserAccountPublicKey();
|
|
2218
2214
|
makerInfo = Array.isArray(makerInfo)
|
|
@@ -2273,7 +2269,7 @@ class DriftClient {
|
|
|
2273
2269
|
return txSig;
|
|
2274
2270
|
}
|
|
2275
2271
|
async getPlaceAndMakePerpOrderIx(orderParams, takerInfo, referrerInfo) {
|
|
2276
|
-
orderParams =
|
|
2272
|
+
orderParams = (0, orderParams_1.getOrderParams)(orderParams, { marketType: types_1.MarketType.PERP });
|
|
2277
2273
|
const userStatsPublicKey = this.getUserStatsAccountPublicKey();
|
|
2278
2274
|
const userAccountPublicKey = await this.getUserAccountPublicKey();
|
|
2279
2275
|
const remainingAccounts = this.getRemainingAccounts({
|
|
@@ -2313,7 +2309,7 @@ class DriftClient {
|
|
|
2313
2309
|
return txSig;
|
|
2314
2310
|
}
|
|
2315
2311
|
async getPlaceAndTakeSpotOrderIx(orderParams, fulfillmentConfig, makerInfo, referrerInfo) {
|
|
2316
|
-
orderParams =
|
|
2312
|
+
orderParams = (0, orderParams_1.getOrderParams)(orderParams, { marketType: types_1.MarketType.SPOT });
|
|
2317
2313
|
const userStatsPublicKey = await this.getUserStatsAccountPublicKey();
|
|
2318
2314
|
const userAccountPublicKey = await this.getUserAccountPublicKey();
|
|
2319
2315
|
const userAccounts = [this.getUserAccount()];
|
|
@@ -2372,7 +2368,7 @@ class DriftClient {
|
|
|
2372
2368
|
return txSig;
|
|
2373
2369
|
}
|
|
2374
2370
|
async getPlaceAndMakeSpotOrderIx(orderParams, takerInfo, fulfillmentConfig, referrerInfo) {
|
|
2375
|
-
orderParams =
|
|
2371
|
+
orderParams = (0, orderParams_1.getOrderParams)(orderParams, { marketType: types_1.MarketType.SPOT });
|
|
2376
2372
|
const userStatsPublicKey = this.getUserStatsAccountPublicKey();
|
|
2377
2373
|
const userAccountPublicKey = await this.getUserAccountPublicKey();
|
|
2378
2374
|
const remainingAccounts = this.getRemainingAccounts({
|
package/lib/idl/drift.json
CHANGED
package/lib/math/utils.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { BN } from '../';
|
|
|
2
2
|
export declare function clampBN(x: BN, min: BN, max: BN): BN;
|
|
3
3
|
export declare const squareRootBN: (n: BN) => BN;
|
|
4
4
|
export declare const divCeil: (a: BN, b: BN) => BN;
|
|
5
|
+
export declare const sigNum: (x: BN) => BN;
|
|
5
6
|
/**
|
|
6
7
|
* calculates the time remaining until the next update based on a rounded, "on-the-hour" update schedule
|
|
7
8
|
* this schedule is used for Perpetual Funding Rate and Revenue -> Insurance Updates
|
package/lib/math/utils.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.timeRemainingUntilUpdate = exports.divCeil = exports.squareRootBN = exports.clampBN = void 0;
|
|
3
|
+
exports.timeRemainingUntilUpdate = exports.sigNum = exports.divCeil = exports.squareRootBN = exports.clampBN = void 0;
|
|
4
4
|
const __1 = require("../");
|
|
5
5
|
function clampBN(x, min, max) {
|
|
6
6
|
return __1.BN.max(min, __1.BN.min(x, max));
|
|
@@ -34,6 +34,10 @@ const divCeil = (a, b) => {
|
|
|
34
34
|
}
|
|
35
35
|
};
|
|
36
36
|
exports.divCeil = divCeil;
|
|
37
|
+
const sigNum = (x) => {
|
|
38
|
+
return x.isNeg() ? new __1.BN(-1) : new __1.BN(1);
|
|
39
|
+
};
|
|
40
|
+
exports.sigNum = sigNum;
|
|
37
41
|
/**
|
|
38
42
|
* calculates the time remaining until the next update based on a rounded, "on-the-hour" update schedule
|
|
39
43
|
* this schedule is used for Perpetual Funding Rate and Revenue -> Insurance Updates
|
package/lib/orderParams.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { OptionalOrderParams, OrderTriggerCondition } from './types';
|
|
1
|
+
import { OptionalOrderParams, OrderParams, OrderTriggerCondition } from './types';
|
|
2
2
|
import { BN } from '@coral-xyz/anchor';
|
|
3
3
|
export declare function getLimitOrderParams(params: Omit<OptionalOrderParams, 'orderType'> & {
|
|
4
4
|
price: BN;
|
|
@@ -13,3 +13,16 @@ export declare function getTriggerLimitOrderParams(params: Omit<OptionalOrderPar
|
|
|
13
13
|
price: BN;
|
|
14
14
|
}): OptionalOrderParams;
|
|
15
15
|
export declare function getMarketOrderParams(params: Omit<OptionalOrderParams, 'orderType'>): OptionalOrderParams;
|
|
16
|
+
/**
|
|
17
|
+
* Creates an OrderParams object with the given OptionalOrderParams and any params to override.
|
|
18
|
+
*
|
|
19
|
+
* example:
|
|
20
|
+
* ```
|
|
21
|
+
* const orderParams = getOrderParams(optionalOrderParams, { marketType: MarketType.PERP });
|
|
22
|
+
* ```
|
|
23
|
+
*
|
|
24
|
+
* @param optionalOrderParams
|
|
25
|
+
* @param overridingParams
|
|
26
|
+
* @returns
|
|
27
|
+
*/
|
|
28
|
+
export declare function getOrderParams(optionalOrderParams: OptionalOrderParams, overridingParams?: Record<string, any>): OrderParams;
|
package/lib/orderParams.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getMarketOrderParams = exports.getTriggerLimitOrderParams = exports.getTriggerMarketOrderParams = exports.getLimitOrderParams = void 0;
|
|
3
|
+
exports.getOrderParams = exports.getMarketOrderParams = exports.getTriggerLimitOrderParams = exports.getTriggerMarketOrderParams = exports.getLimitOrderParams = void 0;
|
|
4
4
|
const types_1 = require("./types");
|
|
5
5
|
function getLimitOrderParams(params) {
|
|
6
6
|
return Object.assign({}, params, {
|
|
@@ -26,3 +26,19 @@ function getMarketOrderParams(params) {
|
|
|
26
26
|
});
|
|
27
27
|
}
|
|
28
28
|
exports.getMarketOrderParams = getMarketOrderParams;
|
|
29
|
+
/**
|
|
30
|
+
* Creates an OrderParams object with the given OptionalOrderParams and any params to override.
|
|
31
|
+
*
|
|
32
|
+
* example:
|
|
33
|
+
* ```
|
|
34
|
+
* const orderParams = getOrderParams(optionalOrderParams, { marketType: MarketType.PERP });
|
|
35
|
+
* ```
|
|
36
|
+
*
|
|
37
|
+
* @param optionalOrderParams
|
|
38
|
+
* @param overridingParams
|
|
39
|
+
* @returns
|
|
40
|
+
*/
|
|
41
|
+
function getOrderParams(optionalOrderParams, overridingParams = {}) {
|
|
42
|
+
return Object.assign({}, types_1.DefaultOrderParams, optionalOrderParams, overridingParams);
|
|
43
|
+
}
|
|
44
|
+
exports.getOrderParams = getOrderParams;
|
package/lib/user.d.ts
CHANGED
|
@@ -42,6 +42,7 @@ export declare class User {
|
|
|
42
42
|
* @returns userSpotPosition
|
|
43
43
|
*/
|
|
44
44
|
getSpotPosition(marketIndex: number): SpotPosition | undefined;
|
|
45
|
+
getEmptySpotPosition(marketIndex: number): SpotPosition;
|
|
45
46
|
/**
|
|
46
47
|
* Returns the token amount for a given market. The spot market precision is based on the token mint decimals.
|
|
47
48
|
* Positive if it is a deposit, negative if it is a borrow.
|
|
@@ -160,6 +161,12 @@ export declare class User {
|
|
|
160
161
|
* @returns : Precision TEN_THOUSAND
|
|
161
162
|
*/
|
|
162
163
|
getLeverage(): BN;
|
|
164
|
+
calculateLeverageFromComponents({ perpLiabilityValue, perpPnl, spotAssetValue, spotLiabilityValue, }: {
|
|
165
|
+
perpLiabilityValue: BN;
|
|
166
|
+
perpPnl: BN;
|
|
167
|
+
spotAssetValue: BN;
|
|
168
|
+
spotLiabilityValue: BN;
|
|
169
|
+
}): BN;
|
|
163
170
|
getLeverageComponents(): {
|
|
164
171
|
perpLiabilityValue: BN;
|
|
165
172
|
perpPnl: BN;
|
|
@@ -252,6 +259,44 @@ export declare class User {
|
|
|
252
259
|
* @returns tradeSizeAllowed : Precision QUOTE_PRECISION
|
|
253
260
|
*/
|
|
254
261
|
getMaxTradeSizeUSDCForSpot(targetMarketIndex: number, direction: PositionDirection, currentQuoteAssetValue?: BN, currentSpotMarketNetValue?: BN): BN;
|
|
262
|
+
/**
|
|
263
|
+
* Calculates the max amount of token that can be swapped from inMarket to outMarket
|
|
264
|
+
* Assumes swap happens at oracle price
|
|
265
|
+
*
|
|
266
|
+
* @param inMarketIndex
|
|
267
|
+
* @param outMarketIndex
|
|
268
|
+
* @param calculateSwap function to similate in to out swa
|
|
269
|
+
* @param iterationLimit how long to run appromixation before erroring out
|
|
270
|
+
*/
|
|
271
|
+
getMaxSwapAmount({ inMarketIndex, outMarketIndex, calculateSwap, iterationLimit, }: {
|
|
272
|
+
inMarketIndex: number;
|
|
273
|
+
outMarketIndex: number;
|
|
274
|
+
calculateSwap?: (inAmount: BN) => BN;
|
|
275
|
+
iterationLimit?: number;
|
|
276
|
+
}): {
|
|
277
|
+
inAmount: BN;
|
|
278
|
+
outAmount: BN;
|
|
279
|
+
leverage: BN;
|
|
280
|
+
};
|
|
281
|
+
cloneAndUpdateSpotPosition(position: SpotPosition, tokenAmount: BN, market: SpotMarketAccount): SpotPosition;
|
|
282
|
+
calculateSpotPositionFreeCollateralContribution(spotPosition: SpotPosition): BN;
|
|
283
|
+
calculateSpotPositionLeverageContribution(spotPosition: SpotPosition): {
|
|
284
|
+
totalAssetValue: BN;
|
|
285
|
+
totalLiabilityValue: BN;
|
|
286
|
+
};
|
|
287
|
+
/**
|
|
288
|
+
* Estimates what the user leverage will be after swap
|
|
289
|
+
* @param inMarketIndex
|
|
290
|
+
* @param outMarketIndex
|
|
291
|
+
* @param inAmount
|
|
292
|
+
* @param outAmount
|
|
293
|
+
*/
|
|
294
|
+
accountLeverageAfterSwap({ inMarketIndex, outMarketIndex, inAmount, outAmount, }: {
|
|
295
|
+
inMarketIndex: number;
|
|
296
|
+
outMarketIndex: number;
|
|
297
|
+
inAmount: BN;
|
|
298
|
+
outAmount: BN;
|
|
299
|
+
}): BN;
|
|
255
300
|
/**
|
|
256
301
|
* Returns the leverage ratio for the account after adding (or subtracting) the given quote size to the given position
|
|
257
302
|
* @param targetMarketIndex
|
package/lib/user.js
CHANGED
|
@@ -80,6 +80,17 @@ class User {
|
|
|
80
80
|
getSpotPosition(marketIndex) {
|
|
81
81
|
return this.getUserAccount().spotPositions.find((position) => position.marketIndex === marketIndex);
|
|
82
82
|
}
|
|
83
|
+
getEmptySpotPosition(marketIndex) {
|
|
84
|
+
return {
|
|
85
|
+
marketIndex,
|
|
86
|
+
scaledBalance: numericConstants_1.ZERO,
|
|
87
|
+
balanceType: _1.SpotBalanceType.DEPOSIT,
|
|
88
|
+
cumulativeDeposits: numericConstants_1.ZERO,
|
|
89
|
+
openAsks: numericConstants_1.ZERO,
|
|
90
|
+
openBids: numericConstants_1.ZERO,
|
|
91
|
+
openOrders: 0,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
83
94
|
/**
|
|
84
95
|
* Returns the token amount for a given market. The spot market precision is based on the token mint decimals.
|
|
85
96
|
* Positive if it is a deposit, negative if it is a borrow.
|
|
@@ -282,7 +293,7 @@ class User {
|
|
|
282
293
|
* @returns : Precision QUOTE_PRECISION
|
|
283
294
|
*/
|
|
284
295
|
getFreeCollateral() {
|
|
285
|
-
const totalCollateral = this.getTotalCollateral();
|
|
296
|
+
const totalCollateral = this.getTotalCollateral('Initial', true);
|
|
286
297
|
const initialMarginRequirement = this.getInitialMarginRequirement();
|
|
287
298
|
const freeCollateral = totalCollateral.sub(initialMarginRequirement);
|
|
288
299
|
return freeCollateral.gte(numericConstants_1.ZERO) ? freeCollateral : numericConstants_1.ZERO;
|
|
@@ -492,7 +503,8 @@ class User {
|
|
|
492
503
|
return assetValue;
|
|
493
504
|
}
|
|
494
505
|
getSpotTokenAmount(marketIndex) {
|
|
495
|
-
|
|
506
|
+
var _a;
|
|
507
|
+
const spotPosition = (_a = this.getSpotPosition(marketIndex)) !== null && _a !== void 0 ? _a : this.getEmptySpotPosition(marketIndex);
|
|
496
508
|
return (0, spotBalance_1.getTokenAmount)(spotPosition.scaledBalance, this.driftClient.getSpotMarketAccount(marketIndex), spotPosition.balanceType);
|
|
497
509
|
}
|
|
498
510
|
getSpotPositionValue(marketIndex, marginCategory, includeOpenOrders, strict = false, now) {
|
|
@@ -671,8 +683,9 @@ class User {
|
|
|
671
683
|
* @returns : Precision TEN_THOUSAND
|
|
672
684
|
*/
|
|
673
685
|
getLeverage() {
|
|
674
|
-
|
|
675
|
-
|
|
686
|
+
return this.calculateLeverageFromComponents(this.getLeverageComponents());
|
|
687
|
+
}
|
|
688
|
+
calculateLeverageFromComponents({ perpLiabilityValue, perpPnl, spotAssetValue, spotLiabilityValue, }) {
|
|
676
689
|
const totalLiabilityValue = perpLiabilityValue.add(spotLiabilityValue);
|
|
677
690
|
const totalAssetValue = spotAssetValue.add(perpPnl);
|
|
678
691
|
const netAssetValue = totalAssetValue.sub(spotLiabilityValue);
|
|
@@ -1089,6 +1102,216 @@ class User {
|
|
|
1089
1102
|
}
|
|
1090
1103
|
return tradeAmount;
|
|
1091
1104
|
}
|
|
1105
|
+
/**
|
|
1106
|
+
* Calculates the max amount of token that can be swapped from inMarket to outMarket
|
|
1107
|
+
* Assumes swap happens at oracle price
|
|
1108
|
+
*
|
|
1109
|
+
* @param inMarketIndex
|
|
1110
|
+
* @param outMarketIndex
|
|
1111
|
+
* @param calculateSwap function to similate in to out swa
|
|
1112
|
+
* @param iterationLimit how long to run appromixation before erroring out
|
|
1113
|
+
*/
|
|
1114
|
+
getMaxSwapAmount({ inMarketIndex, outMarketIndex, calculateSwap, iterationLimit = 1000, }) {
|
|
1115
|
+
const inMarket = this.driftClient.getSpotMarketAccount(inMarketIndex);
|
|
1116
|
+
const outMarket = this.driftClient.getSpotMarketAccount(outMarketIndex);
|
|
1117
|
+
const inOraclePrice = this.getOracleDataForSpotMarket(inMarketIndex).price;
|
|
1118
|
+
const outOraclePrice = this.getOracleDataForSpotMarket(outMarketIndex).price;
|
|
1119
|
+
const inPrecision = new _1.BN(10 ** inMarket.decimals);
|
|
1120
|
+
const outPrecision = new _1.BN(10 ** outMarket.decimals);
|
|
1121
|
+
const outSaferThanIn = inMarket.initialAssetWeight < outMarket.initialAssetWeight;
|
|
1122
|
+
const inSpotPosition = this.getSpotPosition(inMarketIndex) ||
|
|
1123
|
+
this.getEmptySpotPosition(inMarketIndex);
|
|
1124
|
+
const outSpotPosition = this.getSpotPosition(outMarketIndex) ||
|
|
1125
|
+
this.getEmptySpotPosition(outMarketIndex);
|
|
1126
|
+
const freeCollateral = this.getFreeCollateral();
|
|
1127
|
+
const inContributionInitial = this.calculateSpotPositionFreeCollateralContribution(inSpotPosition);
|
|
1128
|
+
const { totalAssetValue: inTotalAssetValueInitial, totalLiabilityValue: inTotalLiabilityValueInitial, } = this.calculateSpotPositionLeverageContribution(inSpotPosition);
|
|
1129
|
+
const outContributionInitial = this.calculateSpotPositionFreeCollateralContribution(outSpotPosition);
|
|
1130
|
+
const { totalAssetValue: outTotalAssetValueInitial, totalLiabilityValue: outTotalLiabilityValueInitial, } = this.calculateSpotPositionLeverageContribution(outSpotPosition);
|
|
1131
|
+
const initialContribution = inContributionInitial.add(outContributionInitial);
|
|
1132
|
+
const { perpLiabilityValue, perpPnl, spotAssetValue, spotLiabilityValue } = this.getLeverageComponents();
|
|
1133
|
+
if (!calculateSwap) {
|
|
1134
|
+
calculateSwap = (inSwap) => {
|
|
1135
|
+
return inSwap
|
|
1136
|
+
.mul(outPrecision)
|
|
1137
|
+
.mul(inOraclePrice)
|
|
1138
|
+
.div(outOraclePrice)
|
|
1139
|
+
.div(inPrecision);
|
|
1140
|
+
};
|
|
1141
|
+
}
|
|
1142
|
+
let inSwap = numericConstants_1.ZERO;
|
|
1143
|
+
let outSwap = numericConstants_1.ZERO;
|
|
1144
|
+
const inTokenAmount = this.getSpotTokenAmount(inMarketIndex);
|
|
1145
|
+
if (freeCollateral.lt(numericConstants_1.ONE)) {
|
|
1146
|
+
if (outSaferThanIn) {
|
|
1147
|
+
inSwap = inTokenAmount;
|
|
1148
|
+
outSwap = calculateSwap(inSwap);
|
|
1149
|
+
}
|
|
1150
|
+
}
|
|
1151
|
+
else {
|
|
1152
|
+
let minSwap = numericConstants_1.ZERO;
|
|
1153
|
+
let maxSwap = freeCollateral
|
|
1154
|
+
.mul(inPrecision)
|
|
1155
|
+
.mul(numericConstants_1.SPOT_MARKET_WEIGHT_PRECISION)
|
|
1156
|
+
.div(numericConstants_1.SPOT_MARKET_WEIGHT_PRECISION.div(new _1.BN(100)))
|
|
1157
|
+
.div(inOraclePrice); // just assume user can go 100x
|
|
1158
|
+
inSwap = maxSwap.div(numericConstants_1.TWO);
|
|
1159
|
+
const error = _1.BN.min(numericConstants_1.QUOTE_PRECISION, freeCollateral.div(new _1.BN(100)));
|
|
1160
|
+
let i = 0;
|
|
1161
|
+
let freeCollateralAfter = freeCollateral;
|
|
1162
|
+
while (freeCollateralAfter.gt(error) || freeCollateralAfter.isNeg()) {
|
|
1163
|
+
outSwap = calculateSwap(inSwap);
|
|
1164
|
+
const inPositionAfter = this.cloneAndUpdateSpotPosition(inSpotPosition, inSwap.neg(), inMarket);
|
|
1165
|
+
const outPositionAfter = this.cloneAndUpdateSpotPosition(outSpotPosition, outSwap, outMarket);
|
|
1166
|
+
const inContributionAfter = this.calculateSpotPositionFreeCollateralContribution(inPositionAfter);
|
|
1167
|
+
const outContributionAfter = this.calculateSpotPositionFreeCollateralContribution(outPositionAfter);
|
|
1168
|
+
const contributionAfter = inContributionAfter.add(outContributionAfter);
|
|
1169
|
+
const contributionDelta = contributionAfter.sub(initialContribution);
|
|
1170
|
+
freeCollateralAfter = freeCollateral.add(contributionDelta);
|
|
1171
|
+
if (freeCollateralAfter.gt(error)) {
|
|
1172
|
+
minSwap = inSwap;
|
|
1173
|
+
inSwap = minSwap.add(maxSwap).div(numericConstants_1.TWO);
|
|
1174
|
+
}
|
|
1175
|
+
else if (freeCollateralAfter.isNeg()) {
|
|
1176
|
+
maxSwap = inSwap;
|
|
1177
|
+
inSwap = minSwap.add(maxSwap).div(numericConstants_1.TWO);
|
|
1178
|
+
}
|
|
1179
|
+
if (i++ > iterationLimit) {
|
|
1180
|
+
throw new Error('getMaxSwapAmount iteration limit reached');
|
|
1181
|
+
}
|
|
1182
|
+
}
|
|
1183
|
+
}
|
|
1184
|
+
const inPositionAfter = this.cloneAndUpdateSpotPosition(inSpotPosition, inSwap.neg(), inMarket);
|
|
1185
|
+
const outPositionAfter = this.cloneAndUpdateSpotPosition(outSpotPosition, outSwap, outMarket);
|
|
1186
|
+
const { totalAssetValue: inTotalAssetValueAfter, totalLiabilityValue: inTotalLiabilityValueAfter, } = this.calculateSpotPositionLeverageContribution(inPositionAfter);
|
|
1187
|
+
const { totalAssetValue: outTotalAssetValueAfter, totalLiabilityValue: outTotalLiabilityValueAfter, } = this.calculateSpotPositionLeverageContribution(outPositionAfter);
|
|
1188
|
+
const spotAssetValueDelta = inTotalAssetValueAfter
|
|
1189
|
+
.add(outTotalAssetValueAfter)
|
|
1190
|
+
.sub(inTotalAssetValueInitial)
|
|
1191
|
+
.sub(outTotalAssetValueInitial);
|
|
1192
|
+
const spotLiabilityValueDelta = inTotalLiabilityValueAfter
|
|
1193
|
+
.add(outTotalLiabilityValueAfter)
|
|
1194
|
+
.sub(inTotalLiabilityValueInitial)
|
|
1195
|
+
.sub(outTotalLiabilityValueInitial);
|
|
1196
|
+
const spotAssetValueAfter = spotAssetValue.add(spotAssetValueDelta);
|
|
1197
|
+
const spotLiabilityValueAfter = spotLiabilityValue.add(spotLiabilityValueDelta);
|
|
1198
|
+
const leverage = this.calculateLeverageFromComponents({
|
|
1199
|
+
perpLiabilityValue,
|
|
1200
|
+
perpPnl,
|
|
1201
|
+
spotAssetValue: spotAssetValueAfter,
|
|
1202
|
+
spotLiabilityValue: spotLiabilityValueAfter,
|
|
1203
|
+
});
|
|
1204
|
+
return { inAmount: inSwap, outAmount: outSwap, leverage };
|
|
1205
|
+
}
|
|
1206
|
+
cloneAndUpdateSpotPosition(position, tokenAmount, market) {
|
|
1207
|
+
const clonedPosition = Object.assign({}, position);
|
|
1208
|
+
if (tokenAmount.eq(numericConstants_1.ZERO)) {
|
|
1209
|
+
return clonedPosition;
|
|
1210
|
+
}
|
|
1211
|
+
const preTokenAmount = (0, _1.getSignedTokenAmount)((0, spotBalance_1.getTokenAmount)(position.scaledBalance, market, position.balanceType), position.balanceType);
|
|
1212
|
+
if ((0, _1.sigNum)(preTokenAmount).eq((0, _1.sigNum)(tokenAmount))) {
|
|
1213
|
+
const scaledBalanceDelta = (0, _1.getBalance)(tokenAmount.abs(), market, position.balanceType);
|
|
1214
|
+
clonedPosition.scaledBalance =
|
|
1215
|
+
clonedPosition.scaledBalance.add(scaledBalanceDelta);
|
|
1216
|
+
return clonedPosition;
|
|
1217
|
+
}
|
|
1218
|
+
const updateDirection = tokenAmount.isNeg()
|
|
1219
|
+
? _1.SpotBalanceType.BORROW
|
|
1220
|
+
: _1.SpotBalanceType.DEPOSIT;
|
|
1221
|
+
if (tokenAmount.abs().gte(preTokenAmount.abs())) {
|
|
1222
|
+
clonedPosition.scaledBalance = (0, _1.getBalance)(tokenAmount.abs().sub(preTokenAmount.abs()), market, updateDirection);
|
|
1223
|
+
clonedPosition.balanceType = updateDirection;
|
|
1224
|
+
}
|
|
1225
|
+
else {
|
|
1226
|
+
const scaledBalanceDelta = (0, _1.getBalance)(tokenAmount.abs(), market, position.balanceType);
|
|
1227
|
+
clonedPosition.scaledBalance =
|
|
1228
|
+
clonedPosition.scaledBalance.sub(scaledBalanceDelta);
|
|
1229
|
+
}
|
|
1230
|
+
return clonedPosition;
|
|
1231
|
+
}
|
|
1232
|
+
calculateSpotPositionFreeCollateralContribution(spotPosition) {
|
|
1233
|
+
let freeCollateralContribution = numericConstants_1.ZERO;
|
|
1234
|
+
const now = new _1.BN(new Date().getTime() / 1000);
|
|
1235
|
+
const strict = true;
|
|
1236
|
+
const marginCategory = 'Initial';
|
|
1237
|
+
const spotMarketAccount = this.driftClient.getSpotMarketAccount(spotPosition.marketIndex);
|
|
1238
|
+
const oraclePriceData = this.getOracleDataForSpotMarket(spotPosition.marketIndex);
|
|
1239
|
+
const [worstCaseTokenAmount, worstCaseQuoteTokenAmount] = (0, spotPosition_1.getWorstCaseTokenAmounts)(spotPosition, spotMarketAccount, oraclePriceData);
|
|
1240
|
+
if (worstCaseTokenAmount.gt(numericConstants_1.ZERO)) {
|
|
1241
|
+
const baseAssetValue = this.getSpotAssetValue(worstCaseTokenAmount, oraclePriceData, spotMarketAccount, marginCategory, strict, now);
|
|
1242
|
+
freeCollateralContribution =
|
|
1243
|
+
freeCollateralContribution.add(baseAssetValue);
|
|
1244
|
+
}
|
|
1245
|
+
else {
|
|
1246
|
+
const baseLiabilityValue = this.getSpotLiabilityValue(worstCaseTokenAmount, oraclePriceData, spotMarketAccount, marginCategory, undefined, strict, now).abs();
|
|
1247
|
+
freeCollateralContribution =
|
|
1248
|
+
freeCollateralContribution.sub(baseLiabilityValue);
|
|
1249
|
+
}
|
|
1250
|
+
freeCollateralContribution.add(worstCaseQuoteTokenAmount);
|
|
1251
|
+
return freeCollateralContribution;
|
|
1252
|
+
}
|
|
1253
|
+
calculateSpotPositionLeverageContribution(spotPosition) {
|
|
1254
|
+
let totalAssetValue = numericConstants_1.ZERO;
|
|
1255
|
+
let totalLiabilityValue = numericConstants_1.ZERO;
|
|
1256
|
+
const now = new _1.BN(new Date().getTime() / 1000);
|
|
1257
|
+
const spotMarketAccount = this.driftClient.getSpotMarketAccount(spotPosition.marketIndex);
|
|
1258
|
+
const oraclePriceData = this.getOracleDataForSpotMarket(spotPosition.marketIndex);
|
|
1259
|
+
const [worstCaseTokenAmount, worstCaseQuoteTokenAmount] = (0, spotPosition_1.getWorstCaseTokenAmounts)(spotPosition, spotMarketAccount, oraclePriceData);
|
|
1260
|
+
if (worstCaseTokenAmount.gt(numericConstants_1.ZERO)) {
|
|
1261
|
+
totalAssetValue = this.getSpotAssetValue(worstCaseTokenAmount, oraclePriceData, spotMarketAccount, undefined, false, now);
|
|
1262
|
+
}
|
|
1263
|
+
else {
|
|
1264
|
+
totalLiabilityValue = this.getSpotLiabilityValue(worstCaseTokenAmount, oraclePriceData, spotMarketAccount, undefined, undefined, false, now).abs();
|
|
1265
|
+
}
|
|
1266
|
+
if (worstCaseQuoteTokenAmount.gt(numericConstants_1.ZERO)) {
|
|
1267
|
+
totalAssetValue = totalAssetValue.add(worstCaseQuoteTokenAmount);
|
|
1268
|
+
}
|
|
1269
|
+
else {
|
|
1270
|
+
totalLiabilityValue = totalLiabilityValue.add(worstCaseQuoteTokenAmount.abs());
|
|
1271
|
+
}
|
|
1272
|
+
return {
|
|
1273
|
+
totalAssetValue,
|
|
1274
|
+
totalLiabilityValue,
|
|
1275
|
+
};
|
|
1276
|
+
}
|
|
1277
|
+
/**
|
|
1278
|
+
* Estimates what the user leverage will be after swap
|
|
1279
|
+
* @param inMarketIndex
|
|
1280
|
+
* @param outMarketIndex
|
|
1281
|
+
* @param inAmount
|
|
1282
|
+
* @param outAmount
|
|
1283
|
+
*/
|
|
1284
|
+
accountLeverageAfterSwap({ inMarketIndex, outMarketIndex, inAmount, outAmount, }) {
|
|
1285
|
+
const inMarket = this.driftClient.getSpotMarketAccount(inMarketIndex);
|
|
1286
|
+
const outMarket = this.driftClient.getSpotMarketAccount(outMarketIndex);
|
|
1287
|
+
const inSpotPosition = this.getSpotPosition(inMarketIndex) ||
|
|
1288
|
+
this.getEmptySpotPosition(inMarketIndex);
|
|
1289
|
+
const outSpotPosition = this.getSpotPosition(outMarketIndex) ||
|
|
1290
|
+
this.getEmptySpotPosition(outMarketIndex);
|
|
1291
|
+
const { totalAssetValue: inTotalAssetValueInitial, totalLiabilityValue: inTotalLiabilityValueInitial, } = this.calculateSpotPositionLeverageContribution(inSpotPosition);
|
|
1292
|
+
const { totalAssetValue: outTotalAssetValueInitial, totalLiabilityValue: outTotalLiabilityValueInitial, } = this.calculateSpotPositionLeverageContribution(outSpotPosition);
|
|
1293
|
+
const { perpLiabilityValue, perpPnl, spotAssetValue, spotLiabilityValue } = this.getLeverageComponents();
|
|
1294
|
+
const inPositionAfter = this.cloneAndUpdateSpotPosition(inSpotPosition, inAmount.abs().neg(), inMarket);
|
|
1295
|
+
const outPositionAfter = this.cloneAndUpdateSpotPosition(outSpotPosition, outAmount.abs(), outMarket);
|
|
1296
|
+
const { totalAssetValue: inTotalAssetValueAfter, totalLiabilityValue: inTotalLiabilityValueAfter, } = this.calculateSpotPositionLeverageContribution(inPositionAfter);
|
|
1297
|
+
const { totalAssetValue: outTotalAssetValueAfter, totalLiabilityValue: outTotalLiabilityValueAfter, } = this.calculateSpotPositionLeverageContribution(outPositionAfter);
|
|
1298
|
+
const spotAssetValueDelta = inTotalAssetValueAfter
|
|
1299
|
+
.add(outTotalAssetValueAfter)
|
|
1300
|
+
.sub(inTotalAssetValueInitial)
|
|
1301
|
+
.sub(outTotalAssetValueInitial);
|
|
1302
|
+
const spotLiabilityValueDelta = inTotalLiabilityValueAfter
|
|
1303
|
+
.add(outTotalLiabilityValueAfter)
|
|
1304
|
+
.sub(inTotalLiabilityValueInitial)
|
|
1305
|
+
.sub(outTotalLiabilityValueInitial);
|
|
1306
|
+
const spotAssetValueAfter = spotAssetValue.add(spotAssetValueDelta);
|
|
1307
|
+
const spotLiabilityValueAfter = spotLiabilityValue.add(spotLiabilityValueDelta);
|
|
1308
|
+
return this.calculateLeverageFromComponents({
|
|
1309
|
+
perpLiabilityValue,
|
|
1310
|
+
perpPnl,
|
|
1311
|
+
spotAssetValue: spotAssetValueAfter,
|
|
1312
|
+
spotLiabilityValue: spotLiabilityValueAfter,
|
|
1313
|
+
});
|
|
1314
|
+
}
|
|
1092
1315
|
// TODO - should this take the price impact of the trade into account for strict accuracy?
|
|
1093
1316
|
/**
|
|
1094
1317
|
* Returns the leverage ratio for the account after adding (or subtracting) the given quote size to the given position
|
package/package.json
CHANGED
package/src/driftClient.ts
CHANGED
|
@@ -27,7 +27,6 @@ import {
|
|
|
27
27
|
MakerInfo,
|
|
28
28
|
TakerInfo,
|
|
29
29
|
OptionalOrderParams,
|
|
30
|
-
DefaultOrderParams,
|
|
31
30
|
OrderType,
|
|
32
31
|
ReferrerInfo,
|
|
33
32
|
MarketType,
|
|
@@ -118,6 +117,7 @@ import { JupiterClient, Route, SwapMode } from './jupiter/jupiterClient';
|
|
|
118
117
|
import { getNonIdleUserFilter } from './memcmp';
|
|
119
118
|
import { UserStatsSubscriptionConfig } from './userStatsConfig';
|
|
120
119
|
import { getMarinadeDepositIx, getMarinadeFinanceProgram } from './marinade';
|
|
120
|
+
import { getOrderParams } from './orderParams';
|
|
121
121
|
|
|
122
122
|
type RemainingAccountParams = {
|
|
123
123
|
userAccounts: UserAccount[];
|
|
@@ -2522,19 +2522,10 @@ export class DriftClient {
|
|
|
2522
2522
|
return txSig;
|
|
2523
2523
|
}
|
|
2524
2524
|
|
|
2525
|
-
getOrderParams(
|
|
2526
|
-
optionalOrderParams: OptionalOrderParams,
|
|
2527
|
-
marketType: MarketType
|
|
2528
|
-
): OrderParams {
|
|
2529
|
-
return Object.assign({}, DefaultOrderParams, optionalOrderParams, {
|
|
2530
|
-
marketType,
|
|
2531
|
-
});
|
|
2532
|
-
}
|
|
2533
|
-
|
|
2534
2525
|
public async getPlacePerpOrderIx(
|
|
2535
2526
|
orderParams: OptionalOrderParams
|
|
2536
2527
|
): Promise<TransactionInstruction> {
|
|
2537
|
-
orderParams =
|
|
2528
|
+
orderParams = getOrderParams(orderParams, { marketType: MarketType.PERP });
|
|
2538
2529
|
const userAccountPublicKey = await this.getUserAccountPublicKey();
|
|
2539
2530
|
|
|
2540
2531
|
const remainingAccounts = this.getRemainingAccounts({
|
|
@@ -3031,7 +3022,7 @@ export class DriftClient {
|
|
|
3031
3022
|
public async getPlaceSpotOrderIx(
|
|
3032
3023
|
orderParams: OptionalOrderParams
|
|
3033
3024
|
): Promise<TransactionInstruction> {
|
|
3034
|
-
orderParams =
|
|
3025
|
+
orderParams = getOrderParams(orderParams, { marketType: MarketType.SPOT });
|
|
3035
3026
|
const userAccountPublicKey = await this.getUserAccountPublicKey();
|
|
3036
3027
|
|
|
3037
3028
|
const remainingAccounts = this.getRemainingAccounts({
|
|
@@ -3891,7 +3882,7 @@ export class DriftClient {
|
|
|
3891
3882
|
makerInfo?: MakerInfo | MakerInfo[],
|
|
3892
3883
|
referrerInfo?: ReferrerInfo
|
|
3893
3884
|
): Promise<TransactionInstruction> {
|
|
3894
|
-
orderParams =
|
|
3885
|
+
orderParams = getOrderParams(orderParams, { marketType: MarketType.PERP });
|
|
3895
3886
|
const userStatsPublicKey = await this.getUserStatsAccountPublicKey();
|
|
3896
3887
|
const userAccountPublicKey = await this.getUserAccountPublicKey();
|
|
3897
3888
|
|
|
@@ -3987,7 +3978,7 @@ export class DriftClient {
|
|
|
3987
3978
|
takerInfo: TakerInfo,
|
|
3988
3979
|
referrerInfo?: ReferrerInfo
|
|
3989
3980
|
): Promise<TransactionInstruction> {
|
|
3990
|
-
orderParams =
|
|
3981
|
+
orderParams = getOrderParams(orderParams, { marketType: MarketType.PERP });
|
|
3991
3982
|
const userStatsPublicKey = this.getUserStatsAccountPublicKey();
|
|
3992
3983
|
const userAccountPublicKey = await this.getUserAccountPublicKey();
|
|
3993
3984
|
|
|
@@ -4059,7 +4050,7 @@ export class DriftClient {
|
|
|
4059
4050
|
makerInfo?: MakerInfo,
|
|
4060
4051
|
referrerInfo?: ReferrerInfo
|
|
4061
4052
|
): Promise<TransactionInstruction> {
|
|
4062
|
-
orderParams =
|
|
4053
|
+
orderParams = getOrderParams(orderParams, { marketType: MarketType.SPOT });
|
|
4063
4054
|
const userStatsPublicKey = await this.getUserStatsAccountPublicKey();
|
|
4064
4055
|
const userAccountPublicKey = await this.getUserAccountPublicKey();
|
|
4065
4056
|
|
|
@@ -4157,7 +4148,7 @@ export class DriftClient {
|
|
|
4157
4148
|
fulfillmentConfig?: SerumV3FulfillmentConfigAccount,
|
|
4158
4149
|
referrerInfo?: ReferrerInfo
|
|
4159
4150
|
): Promise<TransactionInstruction> {
|
|
4160
|
-
orderParams =
|
|
4151
|
+
orderParams = getOrderParams(orderParams, { marketType: MarketType.SPOT });
|
|
4161
4152
|
const userStatsPublicKey = this.getUserStatsAccountPublicKey();
|
|
4162
4153
|
const userAccountPublicKey = await this.getUserAccountPublicKey();
|
|
4163
4154
|
|
package/src/idl/drift.json
CHANGED
package/src/math/utils.ts
CHANGED
|
@@ -34,6 +34,10 @@ export const divCeil = (a: BN, b: BN): BN => {
|
|
|
34
34
|
}
|
|
35
35
|
};
|
|
36
36
|
|
|
37
|
+
export const sigNum = (x: BN): BN => {
|
|
38
|
+
return x.isNeg() ? new BN(-1) : new BN(1);
|
|
39
|
+
};
|
|
40
|
+
|
|
37
41
|
/**
|
|
38
42
|
* calculates the time remaining until the next update based on a rounded, "on-the-hour" update schedule
|
|
39
43
|
* this schedule is used for Perpetual Funding Rate and Revenue -> Insurance Updates
|
package/src/orderParams.ts
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
DefaultOrderParams,
|
|
3
|
+
OptionalOrderParams,
|
|
4
|
+
OrderParams,
|
|
5
|
+
OrderTriggerCondition,
|
|
6
|
+
OrderType,
|
|
7
|
+
} from './types';
|
|
2
8
|
import { BN } from '@coral-xyz/anchor';
|
|
3
9
|
|
|
4
10
|
export function getLimitOrderParams(
|
|
@@ -39,3 +45,27 @@ export function getMarketOrderParams(
|
|
|
39
45
|
orderType: OrderType.MARKET,
|
|
40
46
|
});
|
|
41
47
|
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Creates an OrderParams object with the given OptionalOrderParams and any params to override.
|
|
51
|
+
*
|
|
52
|
+
* example:
|
|
53
|
+
* ```
|
|
54
|
+
* const orderParams = getOrderParams(optionalOrderParams, { marketType: MarketType.PERP });
|
|
55
|
+
* ```
|
|
56
|
+
*
|
|
57
|
+
* @param optionalOrderParams
|
|
58
|
+
* @param overridingParams
|
|
59
|
+
* @returns
|
|
60
|
+
*/
|
|
61
|
+
export function getOrderParams(
|
|
62
|
+
optionalOrderParams: OptionalOrderParams,
|
|
63
|
+
overridingParams: Record<string, any> = {}
|
|
64
|
+
): OrderParams {
|
|
65
|
+
return Object.assign(
|
|
66
|
+
{},
|
|
67
|
+
DefaultOrderParams,
|
|
68
|
+
optionalOrderParams,
|
|
69
|
+
overridingParams
|
|
70
|
+
);
|
|
71
|
+
}
|
package/src/user.ts
CHANGED
|
@@ -28,6 +28,8 @@ import {
|
|
|
28
28
|
OPEN_ORDER_MARGIN_REQUIREMENT,
|
|
29
29
|
FIVE_MINUTE,
|
|
30
30
|
BASE_PRECISION,
|
|
31
|
+
ONE,
|
|
32
|
+
TWO,
|
|
31
33
|
} from './constants/numericConstants';
|
|
32
34
|
import {
|
|
33
35
|
UserAccountSubscriber,
|
|
@@ -50,6 +52,8 @@ import {
|
|
|
50
52
|
calculateSpotMarketMarginRatio,
|
|
51
53
|
getSignedTokenAmount,
|
|
52
54
|
SpotBalanceType,
|
|
55
|
+
sigNum,
|
|
56
|
+
getBalance,
|
|
53
57
|
} from '.';
|
|
54
58
|
import {
|
|
55
59
|
getTokenAmount,
|
|
@@ -165,6 +169,18 @@ export class User {
|
|
|
165
169
|
);
|
|
166
170
|
}
|
|
167
171
|
|
|
172
|
+
getEmptySpotPosition(marketIndex: number): SpotPosition {
|
|
173
|
+
return {
|
|
174
|
+
marketIndex,
|
|
175
|
+
scaledBalance: ZERO,
|
|
176
|
+
balanceType: SpotBalanceType.DEPOSIT,
|
|
177
|
+
cumulativeDeposits: ZERO,
|
|
178
|
+
openAsks: ZERO,
|
|
179
|
+
openBids: ZERO,
|
|
180
|
+
openOrders: 0,
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
|
|
168
184
|
/**
|
|
169
185
|
* Returns the token amount for a given market. The spot market precision is based on the token mint decimals.
|
|
170
186
|
* Positive if it is a deposit, negative if it is a borrow.
|
|
@@ -442,7 +458,7 @@ export class User {
|
|
|
442
458
|
* @returns : Precision QUOTE_PRECISION
|
|
443
459
|
*/
|
|
444
460
|
public getFreeCollateral(): BN {
|
|
445
|
-
const totalCollateral = this.getTotalCollateral();
|
|
461
|
+
const totalCollateral = this.getTotalCollateral('Initial', true);
|
|
446
462
|
const initialMarginRequirement = this.getInitialMarginRequirement();
|
|
447
463
|
const freeCollateral = totalCollateral.sub(initialMarginRequirement);
|
|
448
464
|
return freeCollateral.gte(ZERO) ? freeCollateral : ZERO;
|
|
@@ -919,7 +935,9 @@ export class User {
|
|
|
919
935
|
}
|
|
920
936
|
|
|
921
937
|
public getSpotTokenAmount(marketIndex: number): BN {
|
|
922
|
-
const spotPosition =
|
|
938
|
+
const spotPosition =
|
|
939
|
+
this.getSpotPosition(marketIndex) ??
|
|
940
|
+
this.getEmptySpotPosition(marketIndex);
|
|
923
941
|
return getTokenAmount(
|
|
924
942
|
spotPosition.scaledBalance,
|
|
925
943
|
this.driftClient.getSpotMarketAccount(marketIndex),
|
|
@@ -1231,10 +1249,20 @@ export class User {
|
|
|
1231
1249
|
* @returns : Precision TEN_THOUSAND
|
|
1232
1250
|
*/
|
|
1233
1251
|
public getLeverage(): BN {
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
this.getLeverageComponents();
|
|
1252
|
+
return this.calculateLeverageFromComponents(this.getLeverageComponents());
|
|
1253
|
+
}
|
|
1237
1254
|
|
|
1255
|
+
calculateLeverageFromComponents({
|
|
1256
|
+
perpLiabilityValue,
|
|
1257
|
+
perpPnl,
|
|
1258
|
+
spotAssetValue,
|
|
1259
|
+
spotLiabilityValue,
|
|
1260
|
+
}: {
|
|
1261
|
+
perpLiabilityValue: BN;
|
|
1262
|
+
perpPnl: BN;
|
|
1263
|
+
spotAssetValue: BN;
|
|
1264
|
+
spotLiabilityValue: BN;
|
|
1265
|
+
}): BN {
|
|
1238
1266
|
const totalLiabilityValue = perpLiabilityValue.add(spotLiabilityValue);
|
|
1239
1267
|
const totalAssetValue = spotAssetValue.add(perpPnl);
|
|
1240
1268
|
const netAssetValue = totalAssetValue.sub(spotLiabilityValue);
|
|
@@ -1962,6 +1990,427 @@ export class User {
|
|
|
1962
1990
|
return tradeAmount;
|
|
1963
1991
|
}
|
|
1964
1992
|
|
|
1993
|
+
/**
|
|
1994
|
+
* Calculates the max amount of token that can be swapped from inMarket to outMarket
|
|
1995
|
+
* Assumes swap happens at oracle price
|
|
1996
|
+
*
|
|
1997
|
+
* @param inMarketIndex
|
|
1998
|
+
* @param outMarketIndex
|
|
1999
|
+
* @param calculateSwap function to similate in to out swa
|
|
2000
|
+
* @param iterationLimit how long to run appromixation before erroring out
|
|
2001
|
+
*/
|
|
2002
|
+
public getMaxSwapAmount({
|
|
2003
|
+
inMarketIndex,
|
|
2004
|
+
outMarketIndex,
|
|
2005
|
+
calculateSwap,
|
|
2006
|
+
iterationLimit = 1000,
|
|
2007
|
+
}: {
|
|
2008
|
+
inMarketIndex: number;
|
|
2009
|
+
outMarketIndex: number;
|
|
2010
|
+
calculateSwap?: (inAmount: BN) => BN;
|
|
2011
|
+
iterationLimit?: number;
|
|
2012
|
+
}): { inAmount: BN; outAmount: BN; leverage: BN } {
|
|
2013
|
+
const inMarket = this.driftClient.getSpotMarketAccount(inMarketIndex);
|
|
2014
|
+
const outMarket = this.driftClient.getSpotMarketAccount(outMarketIndex);
|
|
2015
|
+
|
|
2016
|
+
const inOraclePrice = this.getOracleDataForSpotMarket(inMarketIndex).price;
|
|
2017
|
+
const outOraclePrice =
|
|
2018
|
+
this.getOracleDataForSpotMarket(outMarketIndex).price;
|
|
2019
|
+
|
|
2020
|
+
const inPrecision = new BN(10 ** inMarket.decimals);
|
|
2021
|
+
const outPrecision = new BN(10 ** outMarket.decimals);
|
|
2022
|
+
|
|
2023
|
+
const outSaferThanIn =
|
|
2024
|
+
inMarket.initialAssetWeight < outMarket.initialAssetWeight;
|
|
2025
|
+
|
|
2026
|
+
const inSpotPosition =
|
|
2027
|
+
this.getSpotPosition(inMarketIndex) ||
|
|
2028
|
+
this.getEmptySpotPosition(inMarketIndex);
|
|
2029
|
+
const outSpotPosition =
|
|
2030
|
+
this.getSpotPosition(outMarketIndex) ||
|
|
2031
|
+
this.getEmptySpotPosition(outMarketIndex);
|
|
2032
|
+
|
|
2033
|
+
const freeCollateral = this.getFreeCollateral();
|
|
2034
|
+
|
|
2035
|
+
const inContributionInitial =
|
|
2036
|
+
this.calculateSpotPositionFreeCollateralContribution(inSpotPosition);
|
|
2037
|
+
const {
|
|
2038
|
+
totalAssetValue: inTotalAssetValueInitial,
|
|
2039
|
+
totalLiabilityValue: inTotalLiabilityValueInitial,
|
|
2040
|
+
} = this.calculateSpotPositionLeverageContribution(inSpotPosition);
|
|
2041
|
+
const outContributionInitial =
|
|
2042
|
+
this.calculateSpotPositionFreeCollateralContribution(outSpotPosition);
|
|
2043
|
+
const {
|
|
2044
|
+
totalAssetValue: outTotalAssetValueInitial,
|
|
2045
|
+
totalLiabilityValue: outTotalLiabilityValueInitial,
|
|
2046
|
+
} = this.calculateSpotPositionLeverageContribution(outSpotPosition);
|
|
2047
|
+
const initialContribution = inContributionInitial.add(
|
|
2048
|
+
outContributionInitial
|
|
2049
|
+
);
|
|
2050
|
+
|
|
2051
|
+
const { perpLiabilityValue, perpPnl, spotAssetValue, spotLiabilityValue } =
|
|
2052
|
+
this.getLeverageComponents();
|
|
2053
|
+
|
|
2054
|
+
if (!calculateSwap) {
|
|
2055
|
+
calculateSwap = (inSwap: BN) => {
|
|
2056
|
+
return inSwap
|
|
2057
|
+
.mul(outPrecision)
|
|
2058
|
+
.mul(inOraclePrice)
|
|
2059
|
+
.div(outOraclePrice)
|
|
2060
|
+
.div(inPrecision);
|
|
2061
|
+
};
|
|
2062
|
+
}
|
|
2063
|
+
|
|
2064
|
+
let inSwap = ZERO;
|
|
2065
|
+
let outSwap = ZERO;
|
|
2066
|
+
const inTokenAmount = this.getSpotTokenAmount(inMarketIndex);
|
|
2067
|
+
if (freeCollateral.lt(ONE)) {
|
|
2068
|
+
if (outSaferThanIn) {
|
|
2069
|
+
inSwap = inTokenAmount;
|
|
2070
|
+
outSwap = calculateSwap(inSwap);
|
|
2071
|
+
}
|
|
2072
|
+
} else {
|
|
2073
|
+
let minSwap = ZERO;
|
|
2074
|
+
let maxSwap = freeCollateral
|
|
2075
|
+
.mul(inPrecision)
|
|
2076
|
+
.mul(SPOT_MARKET_WEIGHT_PRECISION)
|
|
2077
|
+
.div(SPOT_MARKET_WEIGHT_PRECISION.div(new BN(100)))
|
|
2078
|
+
.div(inOraclePrice); // just assume user can go 100x
|
|
2079
|
+
inSwap = maxSwap.div(TWO);
|
|
2080
|
+
const error = BN.min(QUOTE_PRECISION, freeCollateral.div(new BN(100)));
|
|
2081
|
+
|
|
2082
|
+
let i = 0;
|
|
2083
|
+
let freeCollateralAfter = freeCollateral;
|
|
2084
|
+
while (freeCollateralAfter.gt(error) || freeCollateralAfter.isNeg()) {
|
|
2085
|
+
outSwap = calculateSwap(inSwap);
|
|
2086
|
+
|
|
2087
|
+
const inPositionAfter = this.cloneAndUpdateSpotPosition(
|
|
2088
|
+
inSpotPosition,
|
|
2089
|
+
inSwap.neg(),
|
|
2090
|
+
inMarket
|
|
2091
|
+
);
|
|
2092
|
+
const outPositionAfter = this.cloneAndUpdateSpotPosition(
|
|
2093
|
+
outSpotPosition,
|
|
2094
|
+
outSwap,
|
|
2095
|
+
outMarket
|
|
2096
|
+
);
|
|
2097
|
+
|
|
2098
|
+
const inContributionAfter =
|
|
2099
|
+
this.calculateSpotPositionFreeCollateralContribution(inPositionAfter);
|
|
2100
|
+
const outContributionAfter =
|
|
2101
|
+
this.calculateSpotPositionFreeCollateralContribution(
|
|
2102
|
+
outPositionAfter
|
|
2103
|
+
);
|
|
2104
|
+
|
|
2105
|
+
const contributionAfter = inContributionAfter.add(outContributionAfter);
|
|
2106
|
+
|
|
2107
|
+
const contributionDelta = contributionAfter.sub(initialContribution);
|
|
2108
|
+
|
|
2109
|
+
freeCollateralAfter = freeCollateral.add(contributionDelta);
|
|
2110
|
+
|
|
2111
|
+
if (freeCollateralAfter.gt(error)) {
|
|
2112
|
+
minSwap = inSwap;
|
|
2113
|
+
inSwap = minSwap.add(maxSwap).div(TWO);
|
|
2114
|
+
} else if (freeCollateralAfter.isNeg()) {
|
|
2115
|
+
maxSwap = inSwap;
|
|
2116
|
+
inSwap = minSwap.add(maxSwap).div(TWO);
|
|
2117
|
+
}
|
|
2118
|
+
|
|
2119
|
+
if (i++ > iterationLimit) {
|
|
2120
|
+
throw new Error('getMaxSwapAmount iteration limit reached');
|
|
2121
|
+
}
|
|
2122
|
+
}
|
|
2123
|
+
}
|
|
2124
|
+
|
|
2125
|
+
const inPositionAfter = this.cloneAndUpdateSpotPosition(
|
|
2126
|
+
inSpotPosition,
|
|
2127
|
+
inSwap.neg(),
|
|
2128
|
+
inMarket
|
|
2129
|
+
);
|
|
2130
|
+
const outPositionAfter = this.cloneAndUpdateSpotPosition(
|
|
2131
|
+
outSpotPosition,
|
|
2132
|
+
outSwap,
|
|
2133
|
+
outMarket
|
|
2134
|
+
);
|
|
2135
|
+
|
|
2136
|
+
const {
|
|
2137
|
+
totalAssetValue: inTotalAssetValueAfter,
|
|
2138
|
+
totalLiabilityValue: inTotalLiabilityValueAfter,
|
|
2139
|
+
} = this.calculateSpotPositionLeverageContribution(inPositionAfter);
|
|
2140
|
+
|
|
2141
|
+
const {
|
|
2142
|
+
totalAssetValue: outTotalAssetValueAfter,
|
|
2143
|
+
totalLiabilityValue: outTotalLiabilityValueAfter,
|
|
2144
|
+
} = this.calculateSpotPositionLeverageContribution(outPositionAfter);
|
|
2145
|
+
|
|
2146
|
+
const spotAssetValueDelta = inTotalAssetValueAfter
|
|
2147
|
+
.add(outTotalAssetValueAfter)
|
|
2148
|
+
.sub(inTotalAssetValueInitial)
|
|
2149
|
+
.sub(outTotalAssetValueInitial);
|
|
2150
|
+
const spotLiabilityValueDelta = inTotalLiabilityValueAfter
|
|
2151
|
+
.add(outTotalLiabilityValueAfter)
|
|
2152
|
+
.sub(inTotalLiabilityValueInitial)
|
|
2153
|
+
.sub(outTotalLiabilityValueInitial);
|
|
2154
|
+
|
|
2155
|
+
const spotAssetValueAfter = spotAssetValue.add(spotAssetValueDelta);
|
|
2156
|
+
const spotLiabilityValueAfter = spotLiabilityValue.add(
|
|
2157
|
+
spotLiabilityValueDelta
|
|
2158
|
+
);
|
|
2159
|
+
|
|
2160
|
+
const leverage = this.calculateLeverageFromComponents({
|
|
2161
|
+
perpLiabilityValue,
|
|
2162
|
+
perpPnl,
|
|
2163
|
+
spotAssetValue: spotAssetValueAfter,
|
|
2164
|
+
spotLiabilityValue: spotLiabilityValueAfter,
|
|
2165
|
+
});
|
|
2166
|
+
|
|
2167
|
+
return { inAmount: inSwap, outAmount: outSwap, leverage };
|
|
2168
|
+
}
|
|
2169
|
+
|
|
2170
|
+
cloneAndUpdateSpotPosition(
|
|
2171
|
+
position: SpotPosition,
|
|
2172
|
+
tokenAmount: BN,
|
|
2173
|
+
market: SpotMarketAccount
|
|
2174
|
+
): SpotPosition {
|
|
2175
|
+
const clonedPosition = Object.assign({}, position);
|
|
2176
|
+
if (tokenAmount.eq(ZERO)) {
|
|
2177
|
+
return clonedPosition;
|
|
2178
|
+
}
|
|
2179
|
+
|
|
2180
|
+
const preTokenAmount = getSignedTokenAmount(
|
|
2181
|
+
getTokenAmount(position.scaledBalance, market, position.balanceType),
|
|
2182
|
+
position.balanceType
|
|
2183
|
+
);
|
|
2184
|
+
|
|
2185
|
+
if (sigNum(preTokenAmount).eq(sigNum(tokenAmount))) {
|
|
2186
|
+
const scaledBalanceDelta = getBalance(
|
|
2187
|
+
tokenAmount.abs(),
|
|
2188
|
+
market,
|
|
2189
|
+
position.balanceType
|
|
2190
|
+
);
|
|
2191
|
+
clonedPosition.scaledBalance =
|
|
2192
|
+
clonedPosition.scaledBalance.add(scaledBalanceDelta);
|
|
2193
|
+
return clonedPosition;
|
|
2194
|
+
}
|
|
2195
|
+
|
|
2196
|
+
const updateDirection = tokenAmount.isNeg()
|
|
2197
|
+
? SpotBalanceType.BORROW
|
|
2198
|
+
: SpotBalanceType.DEPOSIT;
|
|
2199
|
+
|
|
2200
|
+
if (tokenAmount.abs().gte(preTokenAmount.abs())) {
|
|
2201
|
+
clonedPosition.scaledBalance = getBalance(
|
|
2202
|
+
tokenAmount.abs().sub(preTokenAmount.abs()),
|
|
2203
|
+
market,
|
|
2204
|
+
updateDirection
|
|
2205
|
+
);
|
|
2206
|
+
clonedPosition.balanceType = updateDirection;
|
|
2207
|
+
} else {
|
|
2208
|
+
const scaledBalanceDelta = getBalance(
|
|
2209
|
+
tokenAmount.abs(),
|
|
2210
|
+
market,
|
|
2211
|
+
position.balanceType
|
|
2212
|
+
);
|
|
2213
|
+
|
|
2214
|
+
clonedPosition.scaledBalance =
|
|
2215
|
+
clonedPosition.scaledBalance.sub(scaledBalanceDelta);
|
|
2216
|
+
}
|
|
2217
|
+
return clonedPosition;
|
|
2218
|
+
}
|
|
2219
|
+
|
|
2220
|
+
calculateSpotPositionFreeCollateralContribution(
|
|
2221
|
+
spotPosition: SpotPosition
|
|
2222
|
+
): BN {
|
|
2223
|
+
let freeCollateralContribution = ZERO;
|
|
2224
|
+
const now = new BN(new Date().getTime() / 1000);
|
|
2225
|
+
const strict = true;
|
|
2226
|
+
const marginCategory = 'Initial';
|
|
2227
|
+
|
|
2228
|
+
const spotMarketAccount: SpotMarketAccount =
|
|
2229
|
+
this.driftClient.getSpotMarketAccount(spotPosition.marketIndex);
|
|
2230
|
+
|
|
2231
|
+
const oraclePriceData = this.getOracleDataForSpotMarket(
|
|
2232
|
+
spotPosition.marketIndex
|
|
2233
|
+
);
|
|
2234
|
+
|
|
2235
|
+
const [worstCaseTokenAmount, worstCaseQuoteTokenAmount] =
|
|
2236
|
+
getWorstCaseTokenAmounts(
|
|
2237
|
+
spotPosition,
|
|
2238
|
+
spotMarketAccount,
|
|
2239
|
+
oraclePriceData
|
|
2240
|
+
);
|
|
2241
|
+
|
|
2242
|
+
if (worstCaseTokenAmount.gt(ZERO)) {
|
|
2243
|
+
const baseAssetValue = this.getSpotAssetValue(
|
|
2244
|
+
worstCaseTokenAmount,
|
|
2245
|
+
oraclePriceData,
|
|
2246
|
+
spotMarketAccount,
|
|
2247
|
+
marginCategory,
|
|
2248
|
+
strict,
|
|
2249
|
+
now
|
|
2250
|
+
);
|
|
2251
|
+
|
|
2252
|
+
freeCollateralContribution =
|
|
2253
|
+
freeCollateralContribution.add(baseAssetValue);
|
|
2254
|
+
} else {
|
|
2255
|
+
const baseLiabilityValue = this.getSpotLiabilityValue(
|
|
2256
|
+
worstCaseTokenAmount,
|
|
2257
|
+
oraclePriceData,
|
|
2258
|
+
spotMarketAccount,
|
|
2259
|
+
marginCategory,
|
|
2260
|
+
undefined,
|
|
2261
|
+
strict,
|
|
2262
|
+
now
|
|
2263
|
+
).abs();
|
|
2264
|
+
|
|
2265
|
+
freeCollateralContribution =
|
|
2266
|
+
freeCollateralContribution.sub(baseLiabilityValue);
|
|
2267
|
+
}
|
|
2268
|
+
|
|
2269
|
+
freeCollateralContribution.add(worstCaseQuoteTokenAmount);
|
|
2270
|
+
|
|
2271
|
+
return freeCollateralContribution;
|
|
2272
|
+
}
|
|
2273
|
+
|
|
2274
|
+
calculateSpotPositionLeverageContribution(spotPosition: SpotPosition): {
|
|
2275
|
+
totalAssetValue: BN;
|
|
2276
|
+
totalLiabilityValue: BN;
|
|
2277
|
+
} {
|
|
2278
|
+
let totalAssetValue = ZERO;
|
|
2279
|
+
let totalLiabilityValue = ZERO;
|
|
2280
|
+
const now = new BN(new Date().getTime() / 1000);
|
|
2281
|
+
|
|
2282
|
+
const spotMarketAccount: SpotMarketAccount =
|
|
2283
|
+
this.driftClient.getSpotMarketAccount(spotPosition.marketIndex);
|
|
2284
|
+
|
|
2285
|
+
const oraclePriceData = this.getOracleDataForSpotMarket(
|
|
2286
|
+
spotPosition.marketIndex
|
|
2287
|
+
);
|
|
2288
|
+
|
|
2289
|
+
const [worstCaseTokenAmount, worstCaseQuoteTokenAmount] =
|
|
2290
|
+
getWorstCaseTokenAmounts(
|
|
2291
|
+
spotPosition,
|
|
2292
|
+
spotMarketAccount,
|
|
2293
|
+
oraclePriceData
|
|
2294
|
+
);
|
|
2295
|
+
|
|
2296
|
+
if (worstCaseTokenAmount.gt(ZERO)) {
|
|
2297
|
+
totalAssetValue = this.getSpotAssetValue(
|
|
2298
|
+
worstCaseTokenAmount,
|
|
2299
|
+
oraclePriceData,
|
|
2300
|
+
spotMarketAccount,
|
|
2301
|
+
undefined,
|
|
2302
|
+
false,
|
|
2303
|
+
now
|
|
2304
|
+
);
|
|
2305
|
+
} else {
|
|
2306
|
+
totalLiabilityValue = this.getSpotLiabilityValue(
|
|
2307
|
+
worstCaseTokenAmount,
|
|
2308
|
+
oraclePriceData,
|
|
2309
|
+
spotMarketAccount,
|
|
2310
|
+
undefined,
|
|
2311
|
+
undefined,
|
|
2312
|
+
false,
|
|
2313
|
+
now
|
|
2314
|
+
).abs();
|
|
2315
|
+
}
|
|
2316
|
+
|
|
2317
|
+
if (worstCaseQuoteTokenAmount.gt(ZERO)) {
|
|
2318
|
+
totalAssetValue = totalAssetValue.add(worstCaseQuoteTokenAmount);
|
|
2319
|
+
} else {
|
|
2320
|
+
totalLiabilityValue = totalLiabilityValue.add(
|
|
2321
|
+
worstCaseQuoteTokenAmount.abs()
|
|
2322
|
+
);
|
|
2323
|
+
}
|
|
2324
|
+
|
|
2325
|
+
return {
|
|
2326
|
+
totalAssetValue,
|
|
2327
|
+
totalLiabilityValue,
|
|
2328
|
+
};
|
|
2329
|
+
}
|
|
2330
|
+
|
|
2331
|
+
/**
|
|
2332
|
+
* Estimates what the user leverage will be after swap
|
|
2333
|
+
* @param inMarketIndex
|
|
2334
|
+
* @param outMarketIndex
|
|
2335
|
+
* @param inAmount
|
|
2336
|
+
* @param outAmount
|
|
2337
|
+
*/
|
|
2338
|
+
public accountLeverageAfterSwap({
|
|
2339
|
+
inMarketIndex,
|
|
2340
|
+
outMarketIndex,
|
|
2341
|
+
inAmount,
|
|
2342
|
+
outAmount,
|
|
2343
|
+
}: {
|
|
2344
|
+
inMarketIndex: number;
|
|
2345
|
+
outMarketIndex: number;
|
|
2346
|
+
inAmount: BN;
|
|
2347
|
+
outAmount: BN;
|
|
2348
|
+
}): BN {
|
|
2349
|
+
const inMarket = this.driftClient.getSpotMarketAccount(inMarketIndex);
|
|
2350
|
+
const outMarket = this.driftClient.getSpotMarketAccount(outMarketIndex);
|
|
2351
|
+
|
|
2352
|
+
const inSpotPosition =
|
|
2353
|
+
this.getSpotPosition(inMarketIndex) ||
|
|
2354
|
+
this.getEmptySpotPosition(inMarketIndex);
|
|
2355
|
+
const outSpotPosition =
|
|
2356
|
+
this.getSpotPosition(outMarketIndex) ||
|
|
2357
|
+
this.getEmptySpotPosition(outMarketIndex);
|
|
2358
|
+
|
|
2359
|
+
const {
|
|
2360
|
+
totalAssetValue: inTotalAssetValueInitial,
|
|
2361
|
+
totalLiabilityValue: inTotalLiabilityValueInitial,
|
|
2362
|
+
} = this.calculateSpotPositionLeverageContribution(inSpotPosition);
|
|
2363
|
+
const {
|
|
2364
|
+
totalAssetValue: outTotalAssetValueInitial,
|
|
2365
|
+
totalLiabilityValue: outTotalLiabilityValueInitial,
|
|
2366
|
+
} = this.calculateSpotPositionLeverageContribution(outSpotPosition);
|
|
2367
|
+
|
|
2368
|
+
const { perpLiabilityValue, perpPnl, spotAssetValue, spotLiabilityValue } =
|
|
2369
|
+
this.getLeverageComponents();
|
|
2370
|
+
|
|
2371
|
+
const inPositionAfter = this.cloneAndUpdateSpotPosition(
|
|
2372
|
+
inSpotPosition,
|
|
2373
|
+
inAmount.abs().neg(),
|
|
2374
|
+
inMarket
|
|
2375
|
+
);
|
|
2376
|
+
const outPositionAfter = this.cloneAndUpdateSpotPosition(
|
|
2377
|
+
outSpotPosition,
|
|
2378
|
+
outAmount.abs(),
|
|
2379
|
+
outMarket
|
|
2380
|
+
);
|
|
2381
|
+
|
|
2382
|
+
const {
|
|
2383
|
+
totalAssetValue: inTotalAssetValueAfter,
|
|
2384
|
+
totalLiabilityValue: inTotalLiabilityValueAfter,
|
|
2385
|
+
} = this.calculateSpotPositionLeverageContribution(inPositionAfter);
|
|
2386
|
+
|
|
2387
|
+
const {
|
|
2388
|
+
totalAssetValue: outTotalAssetValueAfter,
|
|
2389
|
+
totalLiabilityValue: outTotalLiabilityValueAfter,
|
|
2390
|
+
} = this.calculateSpotPositionLeverageContribution(outPositionAfter);
|
|
2391
|
+
|
|
2392
|
+
const spotAssetValueDelta = inTotalAssetValueAfter
|
|
2393
|
+
.add(outTotalAssetValueAfter)
|
|
2394
|
+
.sub(inTotalAssetValueInitial)
|
|
2395
|
+
.sub(outTotalAssetValueInitial);
|
|
2396
|
+
const spotLiabilityValueDelta = inTotalLiabilityValueAfter
|
|
2397
|
+
.add(outTotalLiabilityValueAfter)
|
|
2398
|
+
.sub(inTotalLiabilityValueInitial)
|
|
2399
|
+
.sub(outTotalLiabilityValueInitial);
|
|
2400
|
+
|
|
2401
|
+
const spotAssetValueAfter = spotAssetValue.add(spotAssetValueDelta);
|
|
2402
|
+
const spotLiabilityValueAfter = spotLiabilityValue.add(
|
|
2403
|
+
spotLiabilityValueDelta
|
|
2404
|
+
);
|
|
2405
|
+
|
|
2406
|
+
return this.calculateLeverageFromComponents({
|
|
2407
|
+
perpLiabilityValue,
|
|
2408
|
+
perpPnl,
|
|
2409
|
+
spotAssetValue: spotAssetValueAfter,
|
|
2410
|
+
spotLiabilityValue: spotLiabilityValueAfter,
|
|
2411
|
+
});
|
|
2412
|
+
}
|
|
2413
|
+
|
|
1965
2414
|
// TODO - should this take the price impact of the trade into account for strict accuracy?
|
|
1966
2415
|
|
|
1967
2416
|
/**
|