@drift-labs/sdk 2.60.0-beta.8 → 2.61.0-beta.0
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/accounts/webSocketProgramAccountSubscriber.js +4 -0
- package/lib/auctionSubscriber/auctionSubscriber.js +1 -1
- package/lib/constants/spotMarkets.js +2 -0
- package/lib/idl/drift.json +10 -5
- package/lib/math/trade.d.ts +2 -1
- package/lib/math/trade.js +15 -1
- package/lib/types.d.ts +9 -0
- package/lib/types.js +5 -0
- package/lib/user.d.ts +8 -6
- package/lib/user.js +59 -32
- package/package.json +1 -1
- package/src/accounts/webSocketProgramAccountSubscriber.ts +6 -0
- package/src/auctionSubscriber/auctionSubscriber.ts +1 -1
- package/src/constants/spotMarkets.ts +6 -0
- package/src/idl/drift.json +10 -5
- package/src/math/trade.ts +27 -0
- package/src/types.ts +5 -0
- package/src/user.ts +116 -37
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
2.
|
|
1
|
+
2.61.0-beta.0
|
|
@@ -12,6 +12,9 @@ class WebSocketProgramAccountSubscriber {
|
|
|
12
12
|
this.program = program;
|
|
13
13
|
this.decodeBuffer = decodeBufferFn;
|
|
14
14
|
this.resubTimeoutMs = resubTimeoutMs;
|
|
15
|
+
if (this.resubTimeoutMs < 1000) {
|
|
16
|
+
console.log('resubTimeoutMs should be at least 1000ms to avoid spamming resub');
|
|
17
|
+
}
|
|
15
18
|
this.options = options;
|
|
16
19
|
this.receivingData = false;
|
|
17
20
|
}
|
|
@@ -33,6 +36,7 @@ class WebSocketProgramAccountSubscriber {
|
|
|
33
36
|
}
|
|
34
37
|
}, (_a = this.options.commitment) !== null && _a !== void 0 ? _a : this.program.provider.opts.commitment, this.options.filters);
|
|
35
38
|
if (this.resubTimeoutMs) {
|
|
39
|
+
this.receivingData = true;
|
|
36
40
|
this.setTimeout();
|
|
37
41
|
}
|
|
38
42
|
}
|
|
@@ -15,7 +15,7 @@ class AuctionSubscriber {
|
|
|
15
15
|
if (!this.subscriber) {
|
|
16
16
|
this.subscriber = new webSocketProgramAccountSubscriber_1.WebSocketProgramAccountSubscriber('AuctionSubscriber', 'User', this.driftClient.program, this.driftClient.program.account.user.coder.accounts.decode.bind(this.driftClient.program.account.user.coder.accounts), {
|
|
17
17
|
filters: [(0, memcmp_1.getUserFilter)(), (0, memcmp_1.getUserWithAuctionFilter)()],
|
|
18
|
-
commitment: this.
|
|
18
|
+
commitment: this.opts.commitment,
|
|
19
19
|
}, this.resubTimeoutMs);
|
|
20
20
|
}
|
|
21
21
|
await this.subscriber.subscribe((accountId, data, context) => {
|
|
@@ -139,6 +139,7 @@ exports.MainnetSpotMarkets = [
|
|
|
139
139
|
precision: new __1.BN(10).pow(numericConstants_1.NINE),
|
|
140
140
|
precisionExp: numericConstants_1.NINE,
|
|
141
141
|
serumMarket: new web3_js_1.PublicKey('H87FfmHABiZLRGrDsXRZtqq25YpARzaokCzL1vMYGiep'),
|
|
142
|
+
phoenixMarket: new web3_js_1.PublicKey('BRLLmdtPGuuFn3BU6orYw4KHaohAEptBToi3dwRUnHQZ'),
|
|
142
143
|
},
|
|
143
144
|
{
|
|
144
145
|
symbol: 'WIF',
|
|
@@ -149,6 +150,7 @@ exports.MainnetSpotMarkets = [
|
|
|
149
150
|
precision: new __1.BN(10).pow(numericConstants_1.SIX),
|
|
150
151
|
precisionExp: numericConstants_1.SIX,
|
|
151
152
|
serumMarket: new web3_js_1.PublicKey('2BtDHBTCTUxvdur498ZEcMgimasaFrY5GzLv8wS8XgCb'),
|
|
153
|
+
phoenixMarket: new web3_js_1.PublicKey('6ojSigXF7nDPyhFRgmn3V9ywhYseKF9J32ZrranMGVSX'),
|
|
152
154
|
},
|
|
153
155
|
{
|
|
154
156
|
symbol: 'JUP',
|
package/lib/idl/drift.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "2.
|
|
2
|
+
"version": "2.60.0",
|
|
3
3
|
"name": "drift",
|
|
4
4
|
"instructions": [
|
|
5
5
|
{
|
|
@@ -8364,6 +8364,9 @@
|
|
|
8364
8364
|
},
|
|
8365
8365
|
{
|
|
8366
8366
|
"name": "Borrow"
|
|
8367
|
+
},
|
|
8368
|
+
{
|
|
8369
|
+
"name": "RepayBorrow"
|
|
8367
8370
|
}
|
|
8368
8371
|
]
|
|
8369
8372
|
}
|
|
@@ -8463,6 +8466,9 @@
|
|
|
8463
8466
|
},
|
|
8464
8467
|
{
|
|
8465
8468
|
"name": "OrderFilledWithLPJit"
|
|
8469
|
+
},
|
|
8470
|
+
{
|
|
8471
|
+
"name": "DeriskLp"
|
|
8466
8472
|
}
|
|
8467
8473
|
]
|
|
8468
8474
|
}
|
|
@@ -8480,6 +8486,9 @@
|
|
|
8480
8486
|
},
|
|
8481
8487
|
{
|
|
8482
8488
|
"name": "SettleLiquidity"
|
|
8489
|
+
},
|
|
8490
|
+
{
|
|
8491
|
+
"name": "RemoveLiquidityDerisk"
|
|
8483
8492
|
}
|
|
8484
8493
|
]
|
|
8485
8494
|
}
|
|
@@ -8621,10 +8630,6 @@
|
|
|
8621
8630
|
{
|
|
8622
8631
|
"name": "Liquidation",
|
|
8623
8632
|
"fields": [
|
|
8624
|
-
{
|
|
8625
|
-
"name": "margin_buffer",
|
|
8626
|
-
"type": "u128"
|
|
8627
|
-
},
|
|
8628
8633
|
{
|
|
8629
8634
|
"name": "market_to_track_margin_requirement",
|
|
8630
8635
|
"type": {
|
package/lib/math/trade.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/// <reference types="bn.js" />
|
|
2
|
-
import { PerpMarketAccount, PositionDirection, SpotMarketAccount } from '../types';
|
|
2
|
+
import { PerpMarketAccount, PositionDirection, SpotMarketAccount, UserStatsAccount } from '../types';
|
|
3
3
|
import { BN } from '@coral-xyz/anchor';
|
|
4
4
|
import { AssetType } from './amm';
|
|
5
5
|
import { OraclePriceData } from '../oracles/types';
|
|
@@ -114,3 +114,4 @@ export declare function calculateEstimatedEntryPriceWithL2(assetType: AssetType,
|
|
|
114
114
|
baseFilled: BN;
|
|
115
115
|
quoteFilled: BN;
|
|
116
116
|
};
|
|
117
|
+
export declare function getUser30dRollingVolumeEstimate(userStatsAccount: UserStatsAccount, now?: BN): BN;
|
package/lib/math/trade.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.calculateEstimatedEntryPriceWithL2 = exports.calculateEstimatedSpotEntryPrice = exports.calculateEstimatedPerpEntryPrice = exports.calculateTargetPriceTrade = exports.calculateTradeAcquiredAmounts = exports.calculateTradeSlippage = void 0;
|
|
3
|
+
exports.getUser30dRollingVolumeEstimate = exports.calculateEstimatedEntryPriceWithL2 = exports.calculateEstimatedSpotEntryPrice = exports.calculateEstimatedPerpEntryPrice = exports.calculateTargetPriceTrade = exports.calculateTradeAcquiredAmounts = exports.calculateTradeSlippage = void 0;
|
|
4
4
|
const types_1 = require("../types");
|
|
5
5
|
const anchor_1 = require("@coral-xyz/anchor");
|
|
6
6
|
const assert_1 = require("../assert/assert");
|
|
@@ -621,3 +621,17 @@ function calculateEstimatedEntryPriceWithL2(assetType, amount, direction, basePr
|
|
|
621
621
|
};
|
|
622
622
|
}
|
|
623
623
|
exports.calculateEstimatedEntryPriceWithL2 = calculateEstimatedEntryPriceWithL2;
|
|
624
|
+
function getUser30dRollingVolumeEstimate(userStatsAccount, now) {
|
|
625
|
+
now = now || new anchor_1.BN(new Date().getTime() / 1000);
|
|
626
|
+
const sinceLastTaker = anchor_1.BN.max(now.sub(userStatsAccount.lastTakerVolume30DTs), numericConstants_1.ZERO);
|
|
627
|
+
const sinceLastMaker = anchor_1.BN.max(now.sub(userStatsAccount.lastMakerVolume30DTs), numericConstants_1.ZERO);
|
|
628
|
+
const thirtyDaysInSeconds = new anchor_1.BN(60 * 60 * 24 * 30);
|
|
629
|
+
const last30dVolume = userStatsAccount.takerVolume30D
|
|
630
|
+
.mul(anchor_1.BN.max(thirtyDaysInSeconds.sub(sinceLastTaker), numericConstants_1.ZERO))
|
|
631
|
+
.div(thirtyDaysInSeconds)
|
|
632
|
+
.add(userStatsAccount.makerVolume30D
|
|
633
|
+
.mul(anchor_1.BN.max(thirtyDaysInSeconds.sub(sinceLastMaker), numericConstants_1.ZERO))
|
|
634
|
+
.div(thirtyDaysInSeconds));
|
|
635
|
+
return last30dVolume;
|
|
636
|
+
}
|
|
637
|
+
exports.getUser30dRollingVolumeEstimate = getUser30dRollingVolumeEstimate;
|
package/lib/types.d.ts
CHANGED
|
@@ -252,6 +252,9 @@ export declare class OrderActionExplanation {
|
|
|
252
252
|
static readonly REDUCE_ONLY_ORDER_INCREASED_POSITION: {
|
|
253
253
|
reduceOnlyOrderIncreasedPosition: {};
|
|
254
254
|
};
|
|
255
|
+
static readonly DERISK_LP: {
|
|
256
|
+
deriskLp: {};
|
|
257
|
+
};
|
|
255
258
|
}
|
|
256
259
|
export declare class OrderTriggerCondition {
|
|
257
260
|
static readonly ABOVE: {
|
|
@@ -293,6 +296,9 @@ export declare class DepositExplanation {
|
|
|
293
296
|
static readonly BORROW: {
|
|
294
297
|
borrow: {};
|
|
295
298
|
};
|
|
299
|
+
static readonly REPAY_BORROW: {
|
|
300
|
+
repayBorrow: {};
|
|
301
|
+
};
|
|
296
302
|
}
|
|
297
303
|
export declare class SettlePnlExplanation {
|
|
298
304
|
static readonly NONE: {
|
|
@@ -447,6 +453,9 @@ export declare class LPAction {
|
|
|
447
453
|
static readonly SETTLE_LIQUIDITY: {
|
|
448
454
|
settleLiquidity: {};
|
|
449
455
|
};
|
|
456
|
+
static readonly REMOVE_LIQUIDITY_DERISK: {
|
|
457
|
+
removeLiquidityDerisk: {};
|
|
458
|
+
};
|
|
450
459
|
}
|
|
451
460
|
export type FundingRateRecord = {
|
|
452
461
|
ts: BN;
|
package/lib/types.js
CHANGED
|
@@ -175,6 +175,9 @@ OrderActionExplanation.ORDER_FILLED_WITH_PHOENIX = {
|
|
|
175
175
|
OrderActionExplanation.REDUCE_ONLY_ORDER_INCREASED_POSITION = {
|
|
176
176
|
reduceOnlyOrderIncreasedPosition: {},
|
|
177
177
|
};
|
|
178
|
+
OrderActionExplanation.DERISK_LP = {
|
|
179
|
+
deriskLp: {},
|
|
180
|
+
};
|
|
178
181
|
class OrderTriggerCondition {
|
|
179
182
|
}
|
|
180
183
|
exports.OrderTriggerCondition = OrderTriggerCondition;
|
|
@@ -198,6 +201,7 @@ exports.DepositExplanation = DepositExplanation;
|
|
|
198
201
|
DepositExplanation.NONE = { none: {} };
|
|
199
202
|
DepositExplanation.TRANSFER = { transfer: {} };
|
|
200
203
|
DepositExplanation.BORROW = { borrow: {} };
|
|
204
|
+
DepositExplanation.REPAY_BORROW = { repayBorrow: {} };
|
|
201
205
|
class SettlePnlExplanation {
|
|
202
206
|
}
|
|
203
207
|
exports.SettlePnlExplanation = SettlePnlExplanation;
|
|
@@ -243,6 +247,7 @@ exports.LPAction = LPAction;
|
|
|
243
247
|
LPAction.ADD_LIQUIDITY = { addLiquidity: {} };
|
|
244
248
|
LPAction.REMOVE_LIQUIDITY = { removeLiquidity: {} };
|
|
245
249
|
LPAction.SETTLE_LIQUIDITY = { settleLiquidity: {} };
|
|
250
|
+
LPAction.REMOVE_LIQUIDITY_DERISK = { removeLiquidityDerisk: {} };
|
|
246
251
|
class LiquidationType {
|
|
247
252
|
}
|
|
248
253
|
exports.LiquidationType = LiquidationType;
|
package/lib/user.d.ts
CHANGED
|
@@ -111,7 +111,7 @@ export declare class User {
|
|
|
111
111
|
/**
|
|
112
112
|
* @returns The margin requirement of a certain type (Initial or Maintenance) in USDC. : QUOTE_PRECISION
|
|
113
113
|
*/
|
|
114
|
-
getMarginRequirement(marginCategory: MarginCategory, liquidationBuffer?: BN, strict?: boolean): BN;
|
|
114
|
+
getMarginRequirement(marginCategory: MarginCategory, liquidationBuffer?: BN, strict?: boolean, includeOpenOrders?: boolean): BN;
|
|
115
115
|
/**
|
|
116
116
|
* @returns The initial margin requirement in USDC. : QUOTE_PRECISION
|
|
117
117
|
*/
|
|
@@ -246,18 +246,20 @@ export declare class User {
|
|
|
246
246
|
* Calculate the liquidation price of a perp position, with optional parameter to calculate the liquidation price after a trade
|
|
247
247
|
* @param marketIndex
|
|
248
248
|
* @param positionBaseSizeChange // change in position size to calculate liquidation price for : Precision 10^13
|
|
249
|
+
* @param marginCategory // allow Initial to be passed in if we are trying to calculate price for DLP de-risking
|
|
249
250
|
* @returns Precision : PRICE_PRECISION
|
|
250
251
|
*/
|
|
251
|
-
liquidationPrice(marketIndex: number, positionBaseSizeChange?: BN, estimatedEntryPrice?: BN): BN;
|
|
252
|
-
|
|
253
|
-
|
|
252
|
+
liquidationPrice(marketIndex: number, positionBaseSizeChange?: BN, estimatedEntryPrice?: BN, marginCategory?: MarginCategory): BN;
|
|
253
|
+
calculateEntriesEffectOnFreeCollateral(market: PerpMarketAccount, oraclePrice: BN, perpPosition: PerpPosition, positionBaseSizeChange: BN, estimatedEntryPrice: BN): BN;
|
|
254
|
+
calculateFreeCollateralDeltaForPerp(market: PerpMarketAccount, perpPosition: PerpPosition, positionBaseSizeChange: BN, marginCategory?: MarginCategory): BN | undefined;
|
|
255
|
+
calculateFreeCollateralDeltaForSpot(market: SpotMarketAccount, signedTokenAmount: BN, marginCategory?: MarginCategory): BN;
|
|
254
256
|
/**
|
|
255
257
|
* Calculates the estimated liquidation price for a position after closing a quote amount of the position.
|
|
256
258
|
* @param positionMarketIndex
|
|
257
259
|
* @param closeQuoteAmount
|
|
258
260
|
* @returns : Precision PRICE_PRECISION
|
|
259
261
|
*/
|
|
260
|
-
liquidationPriceAfterClose(positionMarketIndex: number, closeQuoteAmount: BN): BN;
|
|
262
|
+
liquidationPriceAfterClose(positionMarketIndex: number, closeQuoteAmount: BN, estimatedEntryPrice?: BN): BN;
|
|
261
263
|
/**
|
|
262
264
|
* Get the maximum trade size for a given market, taking into account the user's current leverage, positions, collateral, etc.
|
|
263
265
|
*
|
|
@@ -338,7 +340,7 @@ export declare class User {
|
|
|
338
340
|
* @returns leverageRatio : Precision TEN_THOUSAND
|
|
339
341
|
*/
|
|
340
342
|
accountLeverageRatioAfterTrade(targetMarketIndex: number, targetMarketType: MarketType, tradeQuoteAmount: BN, tradeSide: PositionDirection, includeOpenOrders?: boolean): BN;
|
|
341
|
-
getUserFeeTier(marketType: MarketType): import("./types").FeeTier;
|
|
343
|
+
getUserFeeTier(marketType: MarketType, now?: BN): import("./types").FeeTier;
|
|
342
344
|
/**
|
|
343
345
|
* Calculates taker / maker fee (as a percentage, e.g. .001 = 10 basis points) for particular marketType
|
|
344
346
|
* @param marketType
|
package/lib/user.js
CHANGED
|
@@ -432,8 +432,8 @@ class User {
|
|
|
432
432
|
/**
|
|
433
433
|
* @returns The margin requirement of a certain type (Initial or Maintenance) in USDC. : QUOTE_PRECISION
|
|
434
434
|
*/
|
|
435
|
-
getMarginRequirement(marginCategory, liquidationBuffer, strict = false) {
|
|
436
|
-
return this.getTotalPerpPositionValue(marginCategory, liquidationBuffer,
|
|
435
|
+
getMarginRequirement(marginCategory, liquidationBuffer, strict = false, includeOpenOrders = true) {
|
|
436
|
+
return this.getTotalPerpPositionValue(marginCategory, liquidationBuffer, includeOpenOrders, strict).add(this.getSpotMarketLiabilityValue(undefined, marginCategory, liquidationBuffer, includeOpenOrders, strict));
|
|
437
437
|
}
|
|
438
438
|
/**
|
|
439
439
|
* @returns The initial margin requirement in USDC. : QUOTE_PRECISION
|
|
@@ -1080,33 +1080,22 @@ class User {
|
|
|
1080
1080
|
* Calculate the liquidation price of a perp position, with optional parameter to calculate the liquidation price after a trade
|
|
1081
1081
|
* @param marketIndex
|
|
1082
1082
|
* @param positionBaseSizeChange // change in position size to calculate liquidation price for : Precision 10^13
|
|
1083
|
+
* @param marginCategory // allow Initial to be passed in if we are trying to calculate price for DLP de-risking
|
|
1083
1084
|
* @returns Precision : PRICE_PRECISION
|
|
1084
1085
|
*/
|
|
1085
|
-
liquidationPrice(marketIndex, positionBaseSizeChange = numericConstants_1.ZERO, estimatedEntryPrice = numericConstants_1.ZERO) {
|
|
1086
|
-
const totalCollateral = this.getTotalCollateral(
|
|
1087
|
-
const
|
|
1088
|
-
let freeCollateral = _1.BN.max(numericConstants_1.ZERO, totalCollateral.sub(
|
|
1086
|
+
liquidationPrice(marketIndex, positionBaseSizeChange = numericConstants_1.ZERO, estimatedEntryPrice = numericConstants_1.ZERO, marginCategory = 'Maintenance') {
|
|
1087
|
+
const totalCollateral = this.getTotalCollateral(marginCategory);
|
|
1088
|
+
const marginRequirement = this.getMarginRequirement(marginCategory, undefined, false);
|
|
1089
|
+
let freeCollateral = _1.BN.max(numericConstants_1.ZERO, totalCollateral.sub(marginRequirement));
|
|
1089
1090
|
const oracle = this.driftClient.getPerpMarketAccount(marketIndex).amm.oracle;
|
|
1090
1091
|
const oraclePrice = this.driftClient.getOracleDataForPerpMarket(marketIndex).price;
|
|
1091
|
-
// update free collateral to accoutn from pnl based on entry price
|
|
1092
|
-
if (!estimatedEntryPrice.eq(numericConstants_1.ZERO) && !positionBaseSizeChange.eq(numericConstants_1.ZERO)) {
|
|
1093
|
-
const costBasis = oraclePrice
|
|
1094
|
-
.mul(positionBaseSizeChange.abs())
|
|
1095
|
-
.div(numericConstants_1.BASE_PRECISION);
|
|
1096
|
-
const newPositionValue = estimatedEntryPrice
|
|
1097
|
-
.mul(positionBaseSizeChange.abs())
|
|
1098
|
-
.div(numericConstants_1.BASE_PRECISION);
|
|
1099
|
-
if (positionBaseSizeChange.gt(numericConstants_1.ZERO)) {
|
|
1100
|
-
freeCollateral = freeCollateral.add(costBasis.sub(newPositionValue));
|
|
1101
|
-
}
|
|
1102
|
-
else {
|
|
1103
|
-
freeCollateral = freeCollateral.add(newPositionValue.sub(costBasis));
|
|
1104
|
-
}
|
|
1105
|
-
}
|
|
1106
1092
|
const market = this.driftClient.getPerpMarketAccount(marketIndex);
|
|
1107
1093
|
const currentPerpPosition = this.getPerpPositionWithLPSettle(marketIndex, undefined, true)[0] ||
|
|
1108
1094
|
this.getEmptyPosition(marketIndex);
|
|
1109
|
-
|
|
1095
|
+
positionBaseSizeChange = (0, _1.standardizeBaseAssetAmount)(positionBaseSizeChange, market.amm.orderStepSize);
|
|
1096
|
+
const freeCollateralChangeFromNewPosition = this.calculateEntriesEffectOnFreeCollateral(market, oraclePrice, currentPerpPosition, positionBaseSizeChange, estimatedEntryPrice);
|
|
1097
|
+
freeCollateral = freeCollateral.add(freeCollateralChangeFromNewPosition);
|
|
1098
|
+
let freeCollateralDelta = this.calculateFreeCollateralDeltaForPerp(market, currentPerpPosition, positionBaseSizeChange, marginCategory);
|
|
1110
1099
|
if (!freeCollateralDelta) {
|
|
1111
1100
|
return new _1.BN(-1);
|
|
1112
1101
|
}
|
|
@@ -1117,7 +1106,7 @@ class User {
|
|
|
1117
1106
|
const spotPosition = this.getSpotPosition(spotMarketWithSameOracle.marketIndex);
|
|
1118
1107
|
if (spotPosition) {
|
|
1119
1108
|
const signedTokenAmount = (0, _1.getSignedTokenAmount)((0, spotBalance_1.getTokenAmount)(spotPosition.scaledBalance, spotMarketWithSameOracle, spotPosition.balanceType), spotPosition.balanceType);
|
|
1120
|
-
const spotFreeCollateralDelta = this.calculateFreeCollateralDeltaForSpot(spotMarketWithSameOracle, signedTokenAmount);
|
|
1109
|
+
const spotFreeCollateralDelta = this.calculateFreeCollateralDeltaForSpot(spotMarketWithSameOracle, signedTokenAmount, marginCategory);
|
|
1121
1110
|
freeCollateralDelta = freeCollateralDelta.add(spotFreeCollateralDelta || numericConstants_1.ZERO);
|
|
1122
1111
|
}
|
|
1123
1112
|
}
|
|
@@ -1133,13 +1122,51 @@ class User {
|
|
|
1133
1122
|
}
|
|
1134
1123
|
return liqPrice;
|
|
1135
1124
|
}
|
|
1136
|
-
|
|
1125
|
+
calculateEntriesEffectOnFreeCollateral(market, oraclePrice, perpPosition, positionBaseSizeChange, estimatedEntryPrice) {
|
|
1126
|
+
let freeCollateralChange = numericConstants_1.ZERO;
|
|
1127
|
+
// update free collateral to account for change in pnl from new position
|
|
1128
|
+
if (!estimatedEntryPrice.eq(numericConstants_1.ZERO) && !positionBaseSizeChange.eq(numericConstants_1.ZERO)) {
|
|
1129
|
+
const costBasis = oraclePrice
|
|
1130
|
+
.mul(positionBaseSizeChange.abs())
|
|
1131
|
+
.div(numericConstants_1.BASE_PRECISION);
|
|
1132
|
+
const newPositionValue = estimatedEntryPrice
|
|
1133
|
+
.mul(positionBaseSizeChange.abs())
|
|
1134
|
+
.div(numericConstants_1.BASE_PRECISION);
|
|
1135
|
+
if (positionBaseSizeChange.gt(numericConstants_1.ZERO)) {
|
|
1136
|
+
freeCollateralChange = costBasis.sub(newPositionValue);
|
|
1137
|
+
}
|
|
1138
|
+
else {
|
|
1139
|
+
console.log('newPositionValue', newPositionValue.toString());
|
|
1140
|
+
console.log('costBasis', costBasis.toString());
|
|
1141
|
+
freeCollateralChange = newPositionValue.sub(costBasis);
|
|
1142
|
+
}
|
|
1143
|
+
// assume worst fee tier
|
|
1144
|
+
const takerFeeTier = this.driftClient.getStateAccount().perpFeeStructure.feeTiers[0];
|
|
1145
|
+
const takerFee = newPositionValue
|
|
1146
|
+
.muln(takerFeeTier.feeNumerator)
|
|
1147
|
+
.divn(takerFeeTier.feeDenominator);
|
|
1148
|
+
freeCollateralChange = freeCollateralChange.sub(takerFee);
|
|
1149
|
+
}
|
|
1150
|
+
const worstCaseBaseAssetAmount = (0, margin_1.calculateWorstCaseBaseAssetAmount)(perpPosition);
|
|
1151
|
+
const newWorstCaseBaseAssetAmount = worstCaseBaseAssetAmount.add(positionBaseSizeChange);
|
|
1152
|
+
const newMarginRatio = (0, _1.calculateMarketMarginRatio)(market, newWorstCaseBaseAssetAmount.abs(), 'Maintenance');
|
|
1153
|
+
// update free collateral to account for new margin requirement from position change
|
|
1154
|
+
freeCollateralChange = freeCollateralChange.sub(newWorstCaseBaseAssetAmount
|
|
1155
|
+
.abs()
|
|
1156
|
+
.sub(worstCaseBaseAssetAmount.abs())
|
|
1157
|
+
.mul(oraclePrice)
|
|
1158
|
+
.div(numericConstants_1.BASE_PRECISION)
|
|
1159
|
+
.mul(new _1.BN(newMarginRatio))
|
|
1160
|
+
.div(numericConstants_1.MARGIN_PRECISION));
|
|
1161
|
+
return freeCollateralChange;
|
|
1162
|
+
}
|
|
1163
|
+
calculateFreeCollateralDeltaForPerp(market, perpPosition, positionBaseSizeChange, marginCategory = 'Maintenance') {
|
|
1137
1164
|
const currentBaseAssetAmount = perpPosition.baseAssetAmount;
|
|
1138
1165
|
const worstCaseBaseAssetAmount = (0, margin_1.calculateWorstCaseBaseAssetAmount)(perpPosition);
|
|
1139
1166
|
const orderBaseAssetAmount = worstCaseBaseAssetAmount.sub(currentBaseAssetAmount);
|
|
1140
1167
|
const proposedBaseAssetAmount = currentBaseAssetAmount.add(positionBaseSizeChange);
|
|
1141
1168
|
const proposedWorstCaseBaseAssetAmount = worstCaseBaseAssetAmount.add(positionBaseSizeChange);
|
|
1142
|
-
const marginRatio = (0, _1.calculateMarketMarginRatio)(market, proposedWorstCaseBaseAssetAmount.abs(),
|
|
1169
|
+
const marginRatio = (0, _1.calculateMarketMarginRatio)(market, proposedWorstCaseBaseAssetAmount.abs(), marginCategory, this.getUserAccount().maxMarginRatio);
|
|
1143
1170
|
const marginRatioQuotePrecision = new _1.BN(marginRatio)
|
|
1144
1171
|
.mul(numericConstants_1.QUOTE_PRECISION)
|
|
1145
1172
|
.div(numericConstants_1.MARGIN_PRECISION);
|
|
@@ -1165,17 +1192,17 @@ class User {
|
|
|
1165
1192
|
}
|
|
1166
1193
|
return freeCollateralDelta;
|
|
1167
1194
|
}
|
|
1168
|
-
calculateFreeCollateralDeltaForSpot(market, signedTokenAmount) {
|
|
1195
|
+
calculateFreeCollateralDeltaForSpot(market, signedTokenAmount, marginCategory = 'Maintenance') {
|
|
1169
1196
|
const tokenPrecision = new _1.BN(Math.pow(10, market.decimals));
|
|
1170
1197
|
if (signedTokenAmount.gt(numericConstants_1.ZERO)) {
|
|
1171
|
-
const assetWeight = (0, spotBalance_1.calculateAssetWeight)(signedTokenAmount, this.driftClient.getOraclePriceDataAndSlot(market.oracle).data.price, market,
|
|
1198
|
+
const assetWeight = (0, spotBalance_1.calculateAssetWeight)(signedTokenAmount, this.driftClient.getOraclePriceDataAndSlot(market.oracle).data.price, market, marginCategory);
|
|
1172
1199
|
return numericConstants_1.QUOTE_PRECISION.mul(assetWeight)
|
|
1173
1200
|
.div(numericConstants_1.SPOT_MARKET_WEIGHT_PRECISION)
|
|
1174
1201
|
.mul(signedTokenAmount)
|
|
1175
1202
|
.div(tokenPrecision);
|
|
1176
1203
|
}
|
|
1177
1204
|
else {
|
|
1178
|
-
const liabilityWeight = (0, spotBalance_1.calculateLiabilityWeight)(signedTokenAmount.abs(), market,
|
|
1205
|
+
const liabilityWeight = (0, spotBalance_1.calculateLiabilityWeight)(signedTokenAmount.abs(), market, marginCategory);
|
|
1179
1206
|
return numericConstants_1.QUOTE_PRECISION.neg()
|
|
1180
1207
|
.mul(liabilityWeight)
|
|
1181
1208
|
.div(numericConstants_1.SPOT_MARKET_WEIGHT_PRECISION)
|
|
@@ -1189,7 +1216,7 @@ class User {
|
|
|
1189
1216
|
* @param closeQuoteAmount
|
|
1190
1217
|
* @returns : Precision PRICE_PRECISION
|
|
1191
1218
|
*/
|
|
1192
|
-
liquidationPriceAfterClose(positionMarketIndex, closeQuoteAmount) {
|
|
1219
|
+
liquidationPriceAfterClose(positionMarketIndex, closeQuoteAmount, estimatedEntryPrice = numericConstants_1.ZERO) {
|
|
1193
1220
|
const currentPosition = this.getPerpPositionWithLPSettle(positionMarketIndex, undefined, true)[0] || this.getEmptyPosition(positionMarketIndex);
|
|
1194
1221
|
const closeBaseAmount = currentPosition.baseAssetAmount
|
|
1195
1222
|
.mul(closeQuoteAmount)
|
|
@@ -1198,7 +1225,7 @@ class User {
|
|
|
1198
1225
|
.mul(closeQuoteAmount)
|
|
1199
1226
|
.mod(currentPosition.quoteAssetAmount.abs()))
|
|
1200
1227
|
.neg();
|
|
1201
|
-
return this.liquidationPrice(positionMarketIndex, closeBaseAmount);
|
|
1228
|
+
return this.liquidationPrice(positionMarketIndex, closeBaseAmount, estimatedEntryPrice);
|
|
1202
1229
|
}
|
|
1203
1230
|
/**
|
|
1204
1231
|
* Get the maximum trade size for a given market, taking into account the user's current leverage, positions, collateral, etc.
|
|
@@ -1613,14 +1640,14 @@ class User {
|
|
|
1613
1640
|
.div(netAssetValue);
|
|
1614
1641
|
return newLeverage;
|
|
1615
1642
|
}
|
|
1616
|
-
getUserFeeTier(marketType) {
|
|
1643
|
+
getUserFeeTier(marketType, now) {
|
|
1617
1644
|
const state = this.driftClient.getStateAccount();
|
|
1618
1645
|
let feeTierIndex = 0;
|
|
1619
1646
|
if ((0, types_1.isVariant)(marketType, 'perp')) {
|
|
1620
1647
|
const userStatsAccount = this.driftClient
|
|
1621
1648
|
.getUserStats()
|
|
1622
1649
|
.getAccount();
|
|
1623
|
-
const total30dVolume =
|
|
1650
|
+
const total30dVolume = (0, _1.getUser30dRollingVolumeEstimate)(userStatsAccount, now);
|
|
1624
1651
|
const stakedQuoteAssetAmount = userStatsAccount.ifStakedQuoteAssetAmount;
|
|
1625
1652
|
const volumeTiers = [
|
|
1626
1653
|
new _1.BN(100000000).mul(numericConstants_1.QUOTE_PRECISION),
|
package/package.json
CHANGED
|
@@ -42,6 +42,11 @@ export class WebSocketProgramAccountSubscriber<T>
|
|
|
42
42
|
this.program = program;
|
|
43
43
|
this.decodeBuffer = decodeBufferFn;
|
|
44
44
|
this.resubTimeoutMs = resubTimeoutMs;
|
|
45
|
+
if (this.resubTimeoutMs < 1000) {
|
|
46
|
+
console.log(
|
|
47
|
+
'resubTimeoutMs should be at least 1000ms to avoid spamming resub'
|
|
48
|
+
);
|
|
49
|
+
}
|
|
45
50
|
this.options = options;
|
|
46
51
|
this.receivingData = false;
|
|
47
52
|
}
|
|
@@ -73,6 +78,7 @@ export class WebSocketProgramAccountSubscriber<T>
|
|
|
73
78
|
);
|
|
74
79
|
|
|
75
80
|
if (this.resubTimeoutMs) {
|
|
81
|
+
this.receivingData = true;
|
|
76
82
|
this.setTimeout();
|
|
77
83
|
}
|
|
78
84
|
}
|
|
@@ -170,6 +170,9 @@ export const MainnetSpotMarkets: SpotMarketConfig[] = [
|
|
|
170
170
|
precision: new BN(10).pow(NINE),
|
|
171
171
|
precisionExp: NINE,
|
|
172
172
|
serumMarket: new PublicKey('H87FfmHABiZLRGrDsXRZtqq25YpARzaokCzL1vMYGiep'),
|
|
173
|
+
phoenixMarket: new PublicKey(
|
|
174
|
+
'BRLLmdtPGuuFn3BU6orYw4KHaohAEptBToi3dwRUnHQZ'
|
|
175
|
+
),
|
|
173
176
|
},
|
|
174
177
|
{
|
|
175
178
|
symbol: 'WIF',
|
|
@@ -180,6 +183,9 @@ export const MainnetSpotMarkets: SpotMarketConfig[] = [
|
|
|
180
183
|
precision: new BN(10).pow(SIX),
|
|
181
184
|
precisionExp: SIX,
|
|
182
185
|
serumMarket: new PublicKey('2BtDHBTCTUxvdur498ZEcMgimasaFrY5GzLv8wS8XgCb'),
|
|
186
|
+
phoenixMarket: new PublicKey(
|
|
187
|
+
'6ojSigXF7nDPyhFRgmn3V9ywhYseKF9J32ZrranMGVSX'
|
|
188
|
+
),
|
|
183
189
|
},
|
|
184
190
|
{
|
|
185
191
|
symbol: 'JUP',
|
package/src/idl/drift.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "2.
|
|
2
|
+
"version": "2.60.0",
|
|
3
3
|
"name": "drift",
|
|
4
4
|
"instructions": [
|
|
5
5
|
{
|
|
@@ -8364,6 +8364,9 @@
|
|
|
8364
8364
|
},
|
|
8365
8365
|
{
|
|
8366
8366
|
"name": "Borrow"
|
|
8367
|
+
},
|
|
8368
|
+
{
|
|
8369
|
+
"name": "RepayBorrow"
|
|
8367
8370
|
}
|
|
8368
8371
|
]
|
|
8369
8372
|
}
|
|
@@ -8463,6 +8466,9 @@
|
|
|
8463
8466
|
},
|
|
8464
8467
|
{
|
|
8465
8468
|
"name": "OrderFilledWithLPJit"
|
|
8469
|
+
},
|
|
8470
|
+
{
|
|
8471
|
+
"name": "DeriskLp"
|
|
8466
8472
|
}
|
|
8467
8473
|
]
|
|
8468
8474
|
}
|
|
@@ -8480,6 +8486,9 @@
|
|
|
8480
8486
|
},
|
|
8481
8487
|
{
|
|
8482
8488
|
"name": "SettleLiquidity"
|
|
8489
|
+
},
|
|
8490
|
+
{
|
|
8491
|
+
"name": "RemoveLiquidityDerisk"
|
|
8483
8492
|
}
|
|
8484
8493
|
]
|
|
8485
8494
|
}
|
|
@@ -8621,10 +8630,6 @@
|
|
|
8621
8630
|
{
|
|
8622
8631
|
"name": "Liquidation",
|
|
8623
8632
|
"fields": [
|
|
8624
|
-
{
|
|
8625
|
-
"name": "margin_buffer",
|
|
8626
|
-
"type": "u128"
|
|
8627
|
-
},
|
|
8628
8633
|
{
|
|
8629
8634
|
"name": "market_to_track_margin_requirement",
|
|
8630
8635
|
"type": {
|
package/src/math/trade.ts
CHANGED
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
PerpMarketAccount,
|
|
4
4
|
PositionDirection,
|
|
5
5
|
SpotMarketAccount,
|
|
6
|
+
UserStatsAccount,
|
|
6
7
|
} from '../types';
|
|
7
8
|
import { BN } from '@coral-xyz/anchor';
|
|
8
9
|
import { assert } from '../assert/assert';
|
|
@@ -964,3 +965,29 @@ export function calculateEstimatedEntryPriceWithL2(
|
|
|
964
965
|
quoteFilled: cumulativeQuoteFilled,
|
|
965
966
|
};
|
|
966
967
|
}
|
|
968
|
+
|
|
969
|
+
export function getUser30dRollingVolumeEstimate(
|
|
970
|
+
userStatsAccount: UserStatsAccount,
|
|
971
|
+
now?: BN
|
|
972
|
+
) {
|
|
973
|
+
now = now || new BN(new Date().getTime() / 1000);
|
|
974
|
+
const sinceLastTaker = BN.max(
|
|
975
|
+
now.sub(userStatsAccount.lastTakerVolume30DTs),
|
|
976
|
+
ZERO
|
|
977
|
+
);
|
|
978
|
+
const sinceLastMaker = BN.max(
|
|
979
|
+
now.sub(userStatsAccount.lastMakerVolume30DTs),
|
|
980
|
+
ZERO
|
|
981
|
+
);
|
|
982
|
+
const thirtyDaysInSeconds = new BN(60 * 60 * 24 * 30);
|
|
983
|
+
const last30dVolume = userStatsAccount.takerVolume30D
|
|
984
|
+
.mul(BN.max(thirtyDaysInSeconds.sub(sinceLastTaker), ZERO))
|
|
985
|
+
.div(thirtyDaysInSeconds)
|
|
986
|
+
.add(
|
|
987
|
+
userStatsAccount.makerVolume30D
|
|
988
|
+
.mul(BN.max(thirtyDaysInSeconds.sub(sinceLastMaker), ZERO))
|
|
989
|
+
.div(thirtyDaysInSeconds)
|
|
990
|
+
);
|
|
991
|
+
|
|
992
|
+
return last30dVolume;
|
|
993
|
+
}
|
package/src/types.ts
CHANGED
|
@@ -174,6 +174,9 @@ export class OrderActionExplanation {
|
|
|
174
174
|
static readonly REDUCE_ONLY_ORDER_INCREASED_POSITION = {
|
|
175
175
|
reduceOnlyOrderIncreasedPosition: {},
|
|
176
176
|
};
|
|
177
|
+
static readonly DERISK_LP = {
|
|
178
|
+
deriskLp: {},
|
|
179
|
+
};
|
|
177
180
|
}
|
|
178
181
|
|
|
179
182
|
export class OrderTriggerCondition {
|
|
@@ -197,6 +200,7 @@ export class DepositExplanation {
|
|
|
197
200
|
static readonly NONE = { none: {} };
|
|
198
201
|
static readonly TRANSFER = { transfer: {} };
|
|
199
202
|
static readonly BORROW = { borrow: {} };
|
|
203
|
+
static readonly REPAY_BORROW = { repayBorrow: {} };
|
|
200
204
|
}
|
|
201
205
|
|
|
202
206
|
export class SettlePnlExplanation {
|
|
@@ -357,6 +361,7 @@ export class LPAction {
|
|
|
357
361
|
static readonly ADD_LIQUIDITY = { addLiquidity: {} };
|
|
358
362
|
static readonly REMOVE_LIQUIDITY = { removeLiquidity: {} };
|
|
359
363
|
static readonly SETTLE_LIQUIDITY = { settleLiquidity: {} };
|
|
364
|
+
static readonly REMOVE_LIQUIDITY_DERISK = { removeLiquidityDerisk: {} };
|
|
360
365
|
}
|
|
361
366
|
|
|
362
367
|
export type FundingRateRecord = {
|
package/src/user.ts
CHANGED
|
@@ -55,11 +55,13 @@ import {
|
|
|
55
55
|
getSignedTokenAmount,
|
|
56
56
|
getStrictTokenValue,
|
|
57
57
|
getTokenValue,
|
|
58
|
+
getUser30dRollingVolumeEstimate,
|
|
58
59
|
MarketType,
|
|
59
60
|
PositionDirection,
|
|
60
61
|
sigNum,
|
|
61
62
|
SpotBalanceType,
|
|
62
63
|
SpotMarketAccount,
|
|
64
|
+
standardizeBaseAssetAmount,
|
|
63
65
|
} from '.';
|
|
64
66
|
import {
|
|
65
67
|
calculateAssetWeight,
|
|
@@ -673,19 +675,20 @@ export class User {
|
|
|
673
675
|
public getMarginRequirement(
|
|
674
676
|
marginCategory: MarginCategory,
|
|
675
677
|
liquidationBuffer?: BN,
|
|
676
|
-
strict = false
|
|
678
|
+
strict = false,
|
|
679
|
+
includeOpenOrders = true
|
|
677
680
|
): BN {
|
|
678
681
|
return this.getTotalPerpPositionValue(
|
|
679
682
|
marginCategory,
|
|
680
683
|
liquidationBuffer,
|
|
681
|
-
|
|
684
|
+
includeOpenOrders,
|
|
682
685
|
strict
|
|
683
686
|
).add(
|
|
684
687
|
this.getSpotMarketLiabilityValue(
|
|
685
688
|
undefined,
|
|
686
689
|
marginCategory,
|
|
687
690
|
liquidationBuffer,
|
|
688
|
-
|
|
691
|
+
includeOpenOrders,
|
|
689
692
|
strict
|
|
690
693
|
)
|
|
691
694
|
);
|
|
@@ -1969,19 +1972,22 @@ export class User {
|
|
|
1969
1972
|
* Calculate the liquidation price of a perp position, with optional parameter to calculate the liquidation price after a trade
|
|
1970
1973
|
* @param marketIndex
|
|
1971
1974
|
* @param positionBaseSizeChange // change in position size to calculate liquidation price for : Precision 10^13
|
|
1975
|
+
* @param marginCategory // allow Initial to be passed in if we are trying to calculate price for DLP de-risking
|
|
1972
1976
|
* @returns Precision : PRICE_PRECISION
|
|
1973
1977
|
*/
|
|
1974
1978
|
public liquidationPrice(
|
|
1975
1979
|
marketIndex: number,
|
|
1976
1980
|
positionBaseSizeChange: BN = ZERO,
|
|
1977
|
-
estimatedEntryPrice: BN = ZERO
|
|
1981
|
+
estimatedEntryPrice: BN = ZERO,
|
|
1982
|
+
marginCategory: MarginCategory = 'Maintenance'
|
|
1978
1983
|
): BN {
|
|
1979
|
-
const totalCollateral = this.getTotalCollateral(
|
|
1980
|
-
const
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
+
const totalCollateral = this.getTotalCollateral(marginCategory);
|
|
1985
|
+
const marginRequirement = this.getMarginRequirement(
|
|
1986
|
+
marginCategory,
|
|
1987
|
+
undefined,
|
|
1988
|
+
false
|
|
1984
1989
|
);
|
|
1990
|
+
let freeCollateral = BN.max(ZERO, totalCollateral.sub(marginRequirement));
|
|
1985
1991
|
|
|
1986
1992
|
const oracle =
|
|
1987
1993
|
this.driftClient.getPerpMarketAccount(marketIndex).amm.oracle;
|
|
@@ -1989,30 +1995,32 @@ export class User {
|
|
|
1989
1995
|
const oraclePrice =
|
|
1990
1996
|
this.driftClient.getOracleDataForPerpMarket(marketIndex).price;
|
|
1991
1997
|
|
|
1992
|
-
// update free collateral to accoutn from pnl based on entry price
|
|
1993
|
-
if (!estimatedEntryPrice.eq(ZERO) && !positionBaseSizeChange.eq(ZERO)) {
|
|
1994
|
-
const costBasis = oraclePrice
|
|
1995
|
-
.mul(positionBaseSizeChange.abs())
|
|
1996
|
-
.div(BASE_PRECISION);
|
|
1997
|
-
const newPositionValue = estimatedEntryPrice
|
|
1998
|
-
.mul(positionBaseSizeChange.abs())
|
|
1999
|
-
.div(BASE_PRECISION);
|
|
2000
|
-
if (positionBaseSizeChange.gt(ZERO)) {
|
|
2001
|
-
freeCollateral = freeCollateral.add(costBasis.sub(newPositionValue));
|
|
2002
|
-
} else {
|
|
2003
|
-
freeCollateral = freeCollateral.add(newPositionValue.sub(costBasis));
|
|
2004
|
-
}
|
|
2005
|
-
}
|
|
2006
|
-
|
|
2007
1998
|
const market = this.driftClient.getPerpMarketAccount(marketIndex);
|
|
2008
1999
|
const currentPerpPosition =
|
|
2009
2000
|
this.getPerpPositionWithLPSettle(marketIndex, undefined, true)[0] ||
|
|
2010
2001
|
this.getEmptyPosition(marketIndex);
|
|
2011
2002
|
|
|
2003
|
+
positionBaseSizeChange = standardizeBaseAssetAmount(
|
|
2004
|
+
positionBaseSizeChange,
|
|
2005
|
+
market.amm.orderStepSize
|
|
2006
|
+
);
|
|
2007
|
+
|
|
2008
|
+
const freeCollateralChangeFromNewPosition =
|
|
2009
|
+
this.calculateEntriesEffectOnFreeCollateral(
|
|
2010
|
+
market,
|
|
2011
|
+
oraclePrice,
|
|
2012
|
+
currentPerpPosition,
|
|
2013
|
+
positionBaseSizeChange,
|
|
2014
|
+
estimatedEntryPrice
|
|
2015
|
+
);
|
|
2016
|
+
|
|
2017
|
+
freeCollateral = freeCollateral.add(freeCollateralChangeFromNewPosition);
|
|
2018
|
+
|
|
2012
2019
|
let freeCollateralDelta = this.calculateFreeCollateralDeltaForPerp(
|
|
2013
2020
|
market,
|
|
2014
2021
|
currentPerpPosition,
|
|
2015
|
-
positionBaseSizeChange
|
|
2022
|
+
positionBaseSizeChange,
|
|
2023
|
+
marginCategory
|
|
2016
2024
|
);
|
|
2017
2025
|
|
|
2018
2026
|
if (!freeCollateralDelta) {
|
|
@@ -2039,7 +2047,8 @@ export class User {
|
|
|
2039
2047
|
const spotFreeCollateralDelta =
|
|
2040
2048
|
this.calculateFreeCollateralDeltaForSpot(
|
|
2041
2049
|
spotMarketWithSameOracle,
|
|
2042
|
-
signedTokenAmount
|
|
2050
|
+
signedTokenAmount,
|
|
2051
|
+
marginCategory
|
|
2043
2052
|
);
|
|
2044
2053
|
freeCollateralDelta = freeCollateralDelta.add(
|
|
2045
2054
|
spotFreeCollateralDelta || ZERO
|
|
@@ -2064,10 +2073,72 @@ export class User {
|
|
|
2064
2073
|
return liqPrice;
|
|
2065
2074
|
}
|
|
2066
2075
|
|
|
2076
|
+
calculateEntriesEffectOnFreeCollateral(
|
|
2077
|
+
market: PerpMarketAccount,
|
|
2078
|
+
oraclePrice: BN,
|
|
2079
|
+
perpPosition: PerpPosition,
|
|
2080
|
+
positionBaseSizeChange: BN,
|
|
2081
|
+
estimatedEntryPrice: BN
|
|
2082
|
+
): BN {
|
|
2083
|
+
let freeCollateralChange = ZERO;
|
|
2084
|
+
|
|
2085
|
+
// update free collateral to account for change in pnl from new position
|
|
2086
|
+
if (!estimatedEntryPrice.eq(ZERO) && !positionBaseSizeChange.eq(ZERO)) {
|
|
2087
|
+
const costBasis = oraclePrice
|
|
2088
|
+
.mul(positionBaseSizeChange.abs())
|
|
2089
|
+
.div(BASE_PRECISION);
|
|
2090
|
+
const newPositionValue = estimatedEntryPrice
|
|
2091
|
+
.mul(positionBaseSizeChange.abs())
|
|
2092
|
+
.div(BASE_PRECISION);
|
|
2093
|
+
if (positionBaseSizeChange.gt(ZERO)) {
|
|
2094
|
+
freeCollateralChange = costBasis.sub(newPositionValue);
|
|
2095
|
+
} else {
|
|
2096
|
+
console.log('newPositionValue', newPositionValue.toString());
|
|
2097
|
+
console.log('costBasis', costBasis.toString());
|
|
2098
|
+
freeCollateralChange = newPositionValue.sub(costBasis);
|
|
2099
|
+
}
|
|
2100
|
+
|
|
2101
|
+
// assume worst fee tier
|
|
2102
|
+
const takerFeeTier =
|
|
2103
|
+
this.driftClient.getStateAccount().perpFeeStructure.feeTiers[0];
|
|
2104
|
+
const takerFee = newPositionValue
|
|
2105
|
+
.muln(takerFeeTier.feeNumerator)
|
|
2106
|
+
.divn(takerFeeTier.feeDenominator);
|
|
2107
|
+
freeCollateralChange = freeCollateralChange.sub(takerFee);
|
|
2108
|
+
}
|
|
2109
|
+
|
|
2110
|
+
const worstCaseBaseAssetAmount =
|
|
2111
|
+
calculateWorstCaseBaseAssetAmount(perpPosition);
|
|
2112
|
+
|
|
2113
|
+
const newWorstCaseBaseAssetAmount = worstCaseBaseAssetAmount.add(
|
|
2114
|
+
positionBaseSizeChange
|
|
2115
|
+
);
|
|
2116
|
+
|
|
2117
|
+
const newMarginRatio = calculateMarketMarginRatio(
|
|
2118
|
+
market,
|
|
2119
|
+
newWorstCaseBaseAssetAmount.abs(),
|
|
2120
|
+
'Maintenance'
|
|
2121
|
+
);
|
|
2122
|
+
|
|
2123
|
+
// update free collateral to account for new margin requirement from position change
|
|
2124
|
+
freeCollateralChange = freeCollateralChange.sub(
|
|
2125
|
+
newWorstCaseBaseAssetAmount
|
|
2126
|
+
.abs()
|
|
2127
|
+
.sub(worstCaseBaseAssetAmount.abs())
|
|
2128
|
+
.mul(oraclePrice)
|
|
2129
|
+
.div(BASE_PRECISION)
|
|
2130
|
+
.mul(new BN(newMarginRatio))
|
|
2131
|
+
.div(MARGIN_PRECISION)
|
|
2132
|
+
);
|
|
2133
|
+
|
|
2134
|
+
return freeCollateralChange;
|
|
2135
|
+
}
|
|
2136
|
+
|
|
2067
2137
|
calculateFreeCollateralDeltaForPerp(
|
|
2068
2138
|
market: PerpMarketAccount,
|
|
2069
2139
|
perpPosition: PerpPosition,
|
|
2070
|
-
positionBaseSizeChange: BN
|
|
2140
|
+
positionBaseSizeChange: BN,
|
|
2141
|
+
marginCategory: MarginCategory = 'Maintenance'
|
|
2071
2142
|
): BN | undefined {
|
|
2072
2143
|
const currentBaseAssetAmount = perpPosition.baseAssetAmount;
|
|
2073
2144
|
|
|
@@ -2086,7 +2157,8 @@ export class User {
|
|
|
2086
2157
|
const marginRatio = calculateMarketMarginRatio(
|
|
2087
2158
|
market,
|
|
2088
2159
|
proposedWorstCaseBaseAssetAmount.abs(),
|
|
2089
|
-
|
|
2160
|
+
marginCategory,
|
|
2161
|
+
this.getUserAccount().maxMarginRatio
|
|
2090
2162
|
);
|
|
2091
2163
|
const marginRatioQuotePrecision = new BN(marginRatio)
|
|
2092
2164
|
.mul(QUOTE_PRECISION)
|
|
@@ -2121,7 +2193,8 @@ export class User {
|
|
|
2121
2193
|
|
|
2122
2194
|
calculateFreeCollateralDeltaForSpot(
|
|
2123
2195
|
market: SpotMarketAccount,
|
|
2124
|
-
signedTokenAmount: BN
|
|
2196
|
+
signedTokenAmount: BN,
|
|
2197
|
+
marginCategory: MarginCategory = 'Maintenance'
|
|
2125
2198
|
): BN {
|
|
2126
2199
|
const tokenPrecision = new BN(Math.pow(10, market.decimals));
|
|
2127
2200
|
|
|
@@ -2130,7 +2203,7 @@ export class User {
|
|
|
2130
2203
|
signedTokenAmount,
|
|
2131
2204
|
this.driftClient.getOraclePriceDataAndSlot(market.oracle).data.price,
|
|
2132
2205
|
market,
|
|
2133
|
-
|
|
2206
|
+
marginCategory
|
|
2134
2207
|
);
|
|
2135
2208
|
|
|
2136
2209
|
return QUOTE_PRECISION.mul(assetWeight)
|
|
@@ -2141,7 +2214,7 @@ export class User {
|
|
|
2141
2214
|
const liabilityWeight = calculateLiabilityWeight(
|
|
2142
2215
|
signedTokenAmount.abs(),
|
|
2143
2216
|
market,
|
|
2144
|
-
|
|
2217
|
+
marginCategory
|
|
2145
2218
|
);
|
|
2146
2219
|
|
|
2147
2220
|
return QUOTE_PRECISION.neg()
|
|
@@ -2160,7 +2233,8 @@ export class User {
|
|
|
2160
2233
|
*/
|
|
2161
2234
|
public liquidationPriceAfterClose(
|
|
2162
2235
|
positionMarketIndex: number,
|
|
2163
|
-
closeQuoteAmount: BN
|
|
2236
|
+
closeQuoteAmount: BN,
|
|
2237
|
+
estimatedEntryPrice: BN = ZERO
|
|
2164
2238
|
): BN {
|
|
2165
2239
|
const currentPosition =
|
|
2166
2240
|
this.getPerpPositionWithLPSettle(
|
|
@@ -2179,7 +2253,11 @@ export class User {
|
|
|
2179
2253
|
)
|
|
2180
2254
|
.neg();
|
|
2181
2255
|
|
|
2182
|
-
return this.liquidationPrice(
|
|
2256
|
+
return this.liquidationPrice(
|
|
2257
|
+
positionMarketIndex,
|
|
2258
|
+
closeBaseAmount,
|
|
2259
|
+
estimatedEntryPrice
|
|
2260
|
+
);
|
|
2183
2261
|
}
|
|
2184
2262
|
|
|
2185
2263
|
/**
|
|
@@ -2982,7 +3060,7 @@ export class User {
|
|
|
2982
3060
|
return newLeverage;
|
|
2983
3061
|
}
|
|
2984
3062
|
|
|
2985
|
-
public getUserFeeTier(marketType: MarketType) {
|
|
3063
|
+
public getUserFeeTier(marketType: MarketType, now?: BN) {
|
|
2986
3064
|
const state = this.driftClient.getStateAccount();
|
|
2987
3065
|
|
|
2988
3066
|
let feeTierIndex = 0;
|
|
@@ -2991,9 +3069,10 @@ export class User {
|
|
|
2991
3069
|
.getUserStats()
|
|
2992
3070
|
.getAccount();
|
|
2993
3071
|
|
|
2994
|
-
const total30dVolume =
|
|
2995
|
-
userStatsAccount
|
|
2996
|
-
|
|
3072
|
+
const total30dVolume = getUser30dRollingVolumeEstimate(
|
|
3073
|
+
userStatsAccount,
|
|
3074
|
+
now
|
|
3075
|
+
);
|
|
2997
3076
|
|
|
2998
3077
|
const stakedQuoteAssetAmount = userStatsAccount.ifStakedQuoteAssetAmount;
|
|
2999
3078
|
const volumeTiers = [
|