@drift-labs/sdk 2.0.13 → 2.0.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/lib/accounts/pollingDriftClientAccountSubscriber.js +0 -3
- package/lib/accounts/pollingUserStatsAccountSubscriber.js +0 -3
- package/lib/adminClient.d.ts +2 -0
- package/lib/adminClient.js +16 -0
- package/lib/constants/numericConstants.d.ts +2 -0
- package/lib/constants/numericConstants.js +3 -1
- package/lib/dlob/DLOB.d.ts +8 -8
- package/lib/dlob/DLOB.js +227 -246
- package/lib/driftClient.d.ts +19 -0
- package/lib/driftClient.js +32 -0
- package/lib/idl/drift.json +65 -7
- package/lib/math/amm.d.ts +3 -2
- package/lib/math/amm.js +61 -27
- package/lib/math/oracles.d.ts +2 -0
- package/lib/math/oracles.js +23 -1
- package/lib/math/orders.d.ts +1 -2
- package/lib/math/orders.js +4 -20
- package/lib/math/utils.d.ts +1 -0
- package/lib/math/utils.js +5 -1
- package/lib/types.d.ts +6 -2
- package/lib/user.js +10 -6
- package/package.json +1 -1
- package/src/accounts/pollingDriftClientAccountSubscriber.ts +0 -4
- package/src/accounts/pollingUserStatsAccountSubscriber.ts +0 -4
- package/src/adminClient.ts +28 -0
- package/src/constants/numericConstants.ts +3 -0
- package/src/dlob/DLOB.ts +444 -345
- package/src/driftClient.ts +41 -0
- package/src/idl/drift.json +65 -7
- package/src/math/amm.ts +126 -42
- package/src/math/oracles.ts +52 -0
- package/src/math/orders.ts +5 -22
- package/src/math/utils.ts +4 -0
- package/src/types.ts +6 -2
- package/src/user.ts +17 -6
- package/tests/dlob/helpers.ts +4 -2
- package/tests/dlob/test.ts +547 -63
package/lib/driftClient.js
CHANGED
|
@@ -177,10 +177,26 @@ class DriftClient {
|
|
|
177
177
|
getStateAccount() {
|
|
178
178
|
return this.accountSubscriber.getStateAccountAndSlot().data;
|
|
179
179
|
}
|
|
180
|
+
/**
|
|
181
|
+
* Forces a fetch to rpc before returning accounts. Useful for anchor tests.
|
|
182
|
+
*/
|
|
183
|
+
async forceGetStateAccount() {
|
|
184
|
+
await this.accountSubscriber.fetch();
|
|
185
|
+
return this.accountSubscriber.getStateAccountAndSlot().data;
|
|
186
|
+
}
|
|
180
187
|
getPerpMarketAccount(marketIndex) {
|
|
181
188
|
var _a;
|
|
182
189
|
return (_a = this.accountSubscriber.getMarketAccountAndSlot(marketIndex)) === null || _a === void 0 ? void 0 : _a.data;
|
|
183
190
|
}
|
|
191
|
+
/**
|
|
192
|
+
* Forces a fetch to rpc before returning accounts. Useful for anchor tests.
|
|
193
|
+
* @param marketIndex
|
|
194
|
+
*/
|
|
195
|
+
async forceGetPerpMarketAccount(marketIndex) {
|
|
196
|
+
var _a;
|
|
197
|
+
await this.accountSubscriber.fetch();
|
|
198
|
+
return (_a = this.accountSubscriber.getMarketAccountAndSlot(marketIndex)) === null || _a === void 0 ? void 0 : _a.data;
|
|
199
|
+
}
|
|
184
200
|
getPerpMarketAccounts() {
|
|
185
201
|
return this.accountSubscriber
|
|
186
202
|
.getMarketAccountsAndSlots()
|
|
@@ -189,6 +205,14 @@ class DriftClient {
|
|
|
189
205
|
getSpotMarketAccount(marketIndex) {
|
|
190
206
|
return this.accountSubscriber.getSpotMarketAccountAndSlot(marketIndex).data;
|
|
191
207
|
}
|
|
208
|
+
/**
|
|
209
|
+
* Forces a fetch to rpc before returning accounts. Useful for anchor tests.
|
|
210
|
+
* @param marketIndex
|
|
211
|
+
*/
|
|
212
|
+
async forceGetSpotMarketAccount(marketIndex) {
|
|
213
|
+
await this.accountSubscriber.fetch();
|
|
214
|
+
return this.accountSubscriber.getSpotMarketAccountAndSlot(marketIndex).data;
|
|
215
|
+
}
|
|
192
216
|
getSpotMarketAccounts() {
|
|
193
217
|
return this.accountSubscriber
|
|
194
218
|
.getSpotMarketAccountsAndSlots()
|
|
@@ -414,6 +438,14 @@ class DriftClient {
|
|
|
414
438
|
getUserAccount(subAccountId) {
|
|
415
439
|
return this.getUser(subAccountId).getUserAccount();
|
|
416
440
|
}
|
|
441
|
+
/**
|
|
442
|
+
* Forces a fetch to rpc before returning accounts. Useful for anchor tests.
|
|
443
|
+
* @param subAccountId
|
|
444
|
+
*/
|
|
445
|
+
async forceGetUserAccount(subAccountId) {
|
|
446
|
+
await this.getUser(subAccountId).fetchAccounts();
|
|
447
|
+
return this.getUser(subAccountId).getUserAccount();
|
|
448
|
+
}
|
|
417
449
|
getUserAccountAndSlot(subAccountId) {
|
|
418
450
|
return this.getUser(subAccountId).getUserAccountAndSlot();
|
|
419
451
|
}
|
package/lib/idl/drift.json
CHANGED
|
@@ -1432,7 +1432,7 @@
|
|
|
1432
1432
|
},
|
|
1433
1433
|
{
|
|
1434
1434
|
"name": "spotMarket",
|
|
1435
|
-
"isMut":
|
|
1435
|
+
"isMut": true,
|
|
1436
1436
|
"isSigner": false
|
|
1437
1437
|
},
|
|
1438
1438
|
{
|
|
@@ -3233,6 +3233,48 @@
|
|
|
3233
3233
|
}
|
|
3234
3234
|
]
|
|
3235
3235
|
},
|
|
3236
|
+
{
|
|
3237
|
+
"name": "updateInitialPctToLiquidate",
|
|
3238
|
+
"accounts": [
|
|
3239
|
+
{
|
|
3240
|
+
"name": "admin",
|
|
3241
|
+
"isMut": false,
|
|
3242
|
+
"isSigner": true
|
|
3243
|
+
},
|
|
3244
|
+
{
|
|
3245
|
+
"name": "state",
|
|
3246
|
+
"isMut": true,
|
|
3247
|
+
"isSigner": false
|
|
3248
|
+
}
|
|
3249
|
+
],
|
|
3250
|
+
"args": [
|
|
3251
|
+
{
|
|
3252
|
+
"name": "initialPctToLiquidate",
|
|
3253
|
+
"type": "u16"
|
|
3254
|
+
}
|
|
3255
|
+
]
|
|
3256
|
+
},
|
|
3257
|
+
{
|
|
3258
|
+
"name": "updateLiquidationDuration",
|
|
3259
|
+
"accounts": [
|
|
3260
|
+
{
|
|
3261
|
+
"name": "admin",
|
|
3262
|
+
"isMut": false,
|
|
3263
|
+
"isSigner": true
|
|
3264
|
+
},
|
|
3265
|
+
{
|
|
3266
|
+
"name": "state",
|
|
3267
|
+
"isMut": true,
|
|
3268
|
+
"isSigner": false
|
|
3269
|
+
}
|
|
3270
|
+
],
|
|
3271
|
+
"args": [
|
|
3272
|
+
{
|
|
3273
|
+
"name": "liquidationDuration",
|
|
3274
|
+
"type": "u8"
|
|
3275
|
+
}
|
|
3276
|
+
]
|
|
3277
|
+
},
|
|
3236
3278
|
{
|
|
3237
3279
|
"name": "updateOracleGuardRails",
|
|
3238
3280
|
"accounts": [
|
|
@@ -4320,12 +4362,20 @@
|
|
|
4320
4362
|
"defined": "ExchangeStatus"
|
|
4321
4363
|
}
|
|
4322
4364
|
},
|
|
4365
|
+
{
|
|
4366
|
+
"name": "liquidationDuration",
|
|
4367
|
+
"type": "u8"
|
|
4368
|
+
},
|
|
4369
|
+
{
|
|
4370
|
+
"name": "initialPctToLiquidate",
|
|
4371
|
+
"type": "u16"
|
|
4372
|
+
},
|
|
4323
4373
|
{
|
|
4324
4374
|
"name": "padding",
|
|
4325
4375
|
"type": {
|
|
4326
4376
|
"array": [
|
|
4327
4377
|
"u8",
|
|
4328
|
-
|
|
4378
|
+
14
|
|
4329
4379
|
]
|
|
4330
4380
|
}
|
|
4331
4381
|
}
|
|
@@ -4420,8 +4470,8 @@
|
|
|
4420
4470
|
"type": "u64"
|
|
4421
4471
|
},
|
|
4422
4472
|
{
|
|
4423
|
-
"name": "
|
|
4424
|
-
"type": "
|
|
4473
|
+
"name": "liquidationStartSlot",
|
|
4474
|
+
"type": "u64"
|
|
4425
4475
|
},
|
|
4426
4476
|
{
|
|
4427
4477
|
"name": "nextOrderId",
|
|
@@ -5596,11 +5646,11 @@
|
|
|
5596
5646
|
"type": "u64"
|
|
5597
5647
|
},
|
|
5598
5648
|
{
|
|
5599
|
-
"name": "
|
|
5649
|
+
"name": "lastBaseAssetAmountPerLp",
|
|
5600
5650
|
"type": "i64"
|
|
5601
5651
|
},
|
|
5602
5652
|
{
|
|
5603
|
-
"name": "
|
|
5653
|
+
"name": "lastQuoteAssetAmountPerLp",
|
|
5604
5654
|
"type": "i64"
|
|
5605
5655
|
},
|
|
5606
5656
|
{
|
|
@@ -6016,6 +6066,9 @@
|
|
|
6016
6066
|
},
|
|
6017
6067
|
{
|
|
6018
6068
|
"name": "OrderFillWithSerum"
|
|
6069
|
+
},
|
|
6070
|
+
{
|
|
6071
|
+
"name": "NoBorrowLiquidity"
|
|
6019
6072
|
}
|
|
6020
6073
|
]
|
|
6021
6074
|
}
|
|
@@ -8454,11 +8507,16 @@
|
|
|
8454
8507
|
},
|
|
8455
8508
|
{
|
|
8456
8509
|
"code": 6219,
|
|
8510
|
+
"name": "UnableToGetLimitPrice",
|
|
8511
|
+
"msg": "Unable To Get Limit Price"
|
|
8512
|
+
},
|
|
8513
|
+
{
|
|
8514
|
+
"code": 6220,
|
|
8457
8515
|
"name": "InvalidLiquidation",
|
|
8458
8516
|
"msg": "Invalid Liquidation"
|
|
8459
8517
|
},
|
|
8460
8518
|
{
|
|
8461
|
-
"code":
|
|
8519
|
+
"code": 6221,
|
|
8462
8520
|
"name": "SpotFulfillmentConfigDisabled",
|
|
8463
8521
|
"msg": "Spot Fulfullment Config Disabled"
|
|
8464
8522
|
}
|
package/lib/math/amm.d.ts
CHANGED
|
@@ -34,10 +34,11 @@ export declare type AssetType = 'quote' | 'base';
|
|
|
34
34
|
*/
|
|
35
35
|
export declare function calculateAmmReservesAfterSwap(amm: Pick<AMM, 'pegMultiplier' | 'quoteAssetReserve' | 'sqrtK' | 'baseAssetReserve'>, inputAssetType: AssetType, swapAmount: BN, swapDirection: SwapDirection): [BN, BN];
|
|
36
36
|
export declare function calculateMarketOpenBidAsk(baseAssetReserve: BN, minBaseAssetReserve: BN, maxBaseAssetReserve: BN): [BN, BN];
|
|
37
|
-
export declare function calculateInventoryScale(
|
|
37
|
+
export declare function calculateInventoryScale(baseAssetAmountWithAmm: BN, baseAssetReserve: BN, minBaseAssetReserve: BN, maxBaseAssetReserve: BN, directionalSpread: number, maxSpread: number): number;
|
|
38
38
|
export declare function calculateEffectiveLeverage(baseSpread: number, quoteAssetReserve: BN, terminalQuoteAssetReserve: BN, pegMultiplier: BN, netBaseAssetAmount: BN, reservePrice: BN, totalFeeMinusDistributions: BN): number;
|
|
39
39
|
export declare function calculateMaxSpread(marginRatioInitial: number): number;
|
|
40
|
-
export declare function
|
|
40
|
+
export declare function calculateVolSpreadBN(lastOracleConfPct: BN, reservePrice: BN, markStd: BN, oracleStd: BN, longIntensity: BN, shortIntensity: BN, volume24H: BN): [BN, BN];
|
|
41
|
+
export declare function calculateSpreadBN(baseSpread: number, lastOracleReservePriceSpreadPct: BN, lastOracleConfPct: BN, maxSpread: number, quoteAssetReserve: BN, terminalQuoteAssetReserve: BN, pegMultiplier: BN, baseAssetAmountWithAmm: BN, reservePrice: BN, totalFeeMinusDistributions: BN, baseAssetReserve: BN, minBaseAssetReserve: BN, maxBaseAssetReserve: BN, markStd: BN, oracleStd: BN, longIntensity: BN, shortIntensity: BN, volume24H: BN): [number, number];
|
|
41
42
|
export declare function calculateSpread(amm: AMM, direction: PositionDirection, oraclePriceData: OraclePriceData): number;
|
|
42
43
|
export declare function calculateSpreadReserves(amm: AMM, direction: PositionDirection, oraclePriceData: OraclePriceData): {
|
|
43
44
|
baseAssetReserve: BN;
|
package/lib/math/amm.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.calculateMaxBaseAssetAmountFillable = exports.calculateQuoteAssetAmountSwapped = exports.calculateMaxBaseAssetAmountToTrade = exports.calculateTerminalPrice = exports.getSwapDirection = exports.calculateSwapOutput = exports.calculateSpreadReserves = exports.calculateSpread = exports.calculateSpreadBN = exports.calculateMaxSpread = exports.calculateEffectiveLeverage = exports.calculateInventoryScale = exports.calculateMarketOpenBidAsk = exports.calculateAmmReservesAfterSwap = exports.calculatePrice = exports.calculateBidAskPrice = exports.calculateUpdatedAMMSpreadReserves = exports.calculateUpdatedAMM = exports.calculateNewAmm = exports.calculateOptimalPegAndBudget = exports.calculatePegFromTargetPrice = void 0;
|
|
3
|
+
exports.calculateMaxBaseAssetAmountFillable = exports.calculateQuoteAssetAmountSwapped = exports.calculateMaxBaseAssetAmountToTrade = exports.calculateTerminalPrice = exports.getSwapDirection = exports.calculateSwapOutput = exports.calculateSpreadReserves = exports.calculateSpread = exports.calculateSpreadBN = exports.calculateVolSpreadBN = exports.calculateMaxSpread = exports.calculateEffectiveLeverage = exports.calculateInventoryScale = exports.calculateMarketOpenBidAsk = exports.calculateAmmReservesAfterSwap = exports.calculatePrice = exports.calculateBidAskPrice = exports.calculateUpdatedAMMSpreadReserves = exports.calculateUpdatedAMM = exports.calculateNewAmm = exports.calculateOptimalPegAndBudget = exports.calculatePegFromTargetPrice = void 0;
|
|
4
4
|
const anchor_1 = require("@project-serum/anchor");
|
|
5
5
|
const numericConstants_1 = require("../constants/numericConstants");
|
|
6
6
|
const types_1 = require("../types");
|
|
7
7
|
const assert_1 = require("../assert/assert");
|
|
8
8
|
const __1 = require("..");
|
|
9
9
|
const repeg_1 = require("./repeg");
|
|
10
|
+
const oracles_1 = require("./oracles");
|
|
10
11
|
function calculatePegFromTargetPrice(targetPrice, baseAssetReserve, quoteAssetReserve) {
|
|
11
12
|
return anchor_1.BN.max(targetPrice
|
|
12
13
|
.mul(baseAssetReserve)
|
|
@@ -23,15 +24,16 @@ function calculateOptimalPegAndBudget(amm, oraclePriceData) {
|
|
|
23
24
|
const totalFeeLB = amm.totalExchangeFee.div(new anchor_1.BN(2));
|
|
24
25
|
const budget = anchor_1.BN.max(numericConstants_1.ZERO, amm.totalFeeMinusDistributions.sub(totalFeeLB));
|
|
25
26
|
if (budget.lt(prePegCost)) {
|
|
26
|
-
const
|
|
27
|
+
const halfMaxPriceSpread = new anchor_1.BN(amm.maxSpread)
|
|
28
|
+
.div(new anchor_1.BN(2))
|
|
27
29
|
.mul(targetPrice)
|
|
28
30
|
.div(numericConstants_1.BID_ASK_SPREAD_PRECISION);
|
|
29
31
|
let newTargetPrice;
|
|
30
32
|
let newOptimalPeg;
|
|
31
33
|
let newBudget;
|
|
32
34
|
const targetPriceGap = reservePriceBefore.sub(targetPrice);
|
|
33
|
-
if (targetPriceGap.abs().gt(
|
|
34
|
-
const markAdj = targetPriceGap.abs().sub(
|
|
35
|
+
if (targetPriceGap.abs().gt(halfMaxPriceSpread)) {
|
|
36
|
+
const markAdj = targetPriceGap.abs().sub(halfMaxPriceSpread);
|
|
35
37
|
if (targetPriceGap.lt(new anchor_1.BN(0))) {
|
|
36
38
|
newTargetPrice = reservePriceBefore.add(markAdj);
|
|
37
39
|
}
|
|
@@ -91,6 +93,8 @@ function calculateUpdatedAMM(amm, oraclePriceData) {
|
|
|
91
93
|
newAmm.terminalQuoteAssetReserve = newQuoteAssetReserve;
|
|
92
94
|
newAmm.totalFeeMinusDistributions =
|
|
93
95
|
newAmm.totalFeeMinusDistributions.sub(prepegCost);
|
|
96
|
+
newAmm.netRevenueSinceLastFunding =
|
|
97
|
+
newAmm.netRevenueSinceLastFunding.sub(prepegCost);
|
|
94
98
|
return newAmm;
|
|
95
99
|
}
|
|
96
100
|
exports.calculateUpdatedAMM = calculateUpdatedAMM;
|
|
@@ -168,14 +172,14 @@ exports.calculateAmmReservesAfterSwap = calculateAmmReservesAfterSwap;
|
|
|
168
172
|
function calculateMarketOpenBidAsk(baseAssetReserve, minBaseAssetReserve, maxBaseAssetReserve) {
|
|
169
173
|
// open orders
|
|
170
174
|
let openAsks;
|
|
171
|
-
if (maxBaseAssetReserve
|
|
175
|
+
if (maxBaseAssetReserve.gt(baseAssetReserve)) {
|
|
172
176
|
openAsks = maxBaseAssetReserve.sub(baseAssetReserve).mul(new anchor_1.BN(-1));
|
|
173
177
|
}
|
|
174
178
|
else {
|
|
175
179
|
openAsks = numericConstants_1.ZERO;
|
|
176
180
|
}
|
|
177
181
|
let openBids;
|
|
178
|
-
if (minBaseAssetReserve
|
|
182
|
+
if (minBaseAssetReserve.lt(baseAssetReserve)) {
|
|
179
183
|
openBids = baseAssetReserve.sub(minBaseAssetReserve);
|
|
180
184
|
}
|
|
181
185
|
else {
|
|
@@ -184,13 +188,26 @@ function calculateMarketOpenBidAsk(baseAssetReserve, minBaseAssetReserve, maxBas
|
|
|
184
188
|
return [openBids, openAsks];
|
|
185
189
|
}
|
|
186
190
|
exports.calculateMarketOpenBidAsk = calculateMarketOpenBidAsk;
|
|
187
|
-
function calculateInventoryScale(
|
|
188
|
-
|
|
191
|
+
function calculateInventoryScale(baseAssetAmountWithAmm, baseAssetReserve, minBaseAssetReserve, maxBaseAssetReserve, directionalSpread, maxSpread) {
|
|
192
|
+
if (baseAssetAmountWithAmm.eq(numericConstants_1.ZERO)) {
|
|
193
|
+
return 0;
|
|
194
|
+
}
|
|
195
|
+
const defaultLargeBidAskFactor = numericConstants_1.BID_ASK_SPREAD_PRECISION.mul(new anchor_1.BN(10));
|
|
189
196
|
// inventory skew
|
|
190
197
|
const [openBids, openAsks] = calculateMarketOpenBidAsk(baseAssetReserve, minBaseAssetReserve, maxBaseAssetReserve);
|
|
191
198
|
const minSideLiquidity = anchor_1.BN.max(new anchor_1.BN(1), anchor_1.BN.min(openBids.abs(), openAsks.abs()));
|
|
192
|
-
const
|
|
193
|
-
|
|
199
|
+
const inventoryScaleMax = anchor_1.BN.max(defaultLargeBidAskFactor, new anchor_1.BN(maxSpread / 2)
|
|
200
|
+
.mul(numericConstants_1.BID_ASK_SPREAD_PRECISION)
|
|
201
|
+
.div(new anchor_1.BN(Math.max(directionalSpread, 1)))).toNumber() / numericConstants_1.BID_ASK_SPREAD_PRECISION.toNumber();
|
|
202
|
+
const inventoryScale = baseAssetAmountWithAmm
|
|
203
|
+
.mul(anchor_1.BN.max(baseAssetAmountWithAmm.abs(), numericConstants_1.BASE_PRECISION))
|
|
204
|
+
.div(numericConstants_1.BASE_PRECISION)
|
|
205
|
+
.mul(defaultLargeBidAskFactor)
|
|
206
|
+
.div(minSideLiquidity)
|
|
207
|
+
.abs()
|
|
208
|
+
.toNumber() / numericConstants_1.BID_ASK_SPREAD_PRECISION.toNumber();
|
|
209
|
+
const inventorySpreadScale = Math.min(inventoryScaleMax, 1 + inventoryScale);
|
|
210
|
+
return inventorySpreadScale;
|
|
194
211
|
}
|
|
195
212
|
exports.calculateInventoryScale = calculateInventoryScale;
|
|
196
213
|
function calculateEffectiveLeverage(baseSpread, quoteAssetReserve, terminalQuoteAssetReserve, pegMultiplier, netBaseAssetAmount, reservePrice, totalFeeMinusDistributions) {
|
|
@@ -215,31 +232,46 @@ function calculateMaxSpread(marginRatioInitial) {
|
|
|
215
232
|
return maxTargetSpread;
|
|
216
233
|
}
|
|
217
234
|
exports.calculateMaxSpread = calculateMaxSpread;
|
|
218
|
-
function
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
235
|
+
function calculateVolSpreadBN(lastOracleConfPct, reservePrice, markStd, oracleStd, longIntensity, shortIntensity, volume24H) {
|
|
236
|
+
const marketAvgStdPct = markStd
|
|
237
|
+
.add(oracleStd)
|
|
238
|
+
.mul(numericConstants_1.PERCENTAGE_PRECISION)
|
|
239
|
+
.div(reservePrice.mul(new anchor_1.BN(2)));
|
|
240
|
+
const volSpread = anchor_1.BN.max(lastOracleConfPct, marketAvgStdPct.div(new anchor_1.BN(2)));
|
|
241
|
+
const clampMax = numericConstants_1.PERCENTAGE_PRECISION.mul(new anchor_1.BN(16)).div(new anchor_1.BN(10));
|
|
242
|
+
const clampMin = numericConstants_1.PERCENTAGE_PRECISION.div(new anchor_1.BN(10));
|
|
243
|
+
const longVolSpreadFactor = (0, __1.clampBN)(longIntensity.mul(numericConstants_1.PERCENTAGE_PRECISION).div(anchor_1.BN.max(numericConstants_1.ONE, volume24H)), clampMin, clampMax);
|
|
244
|
+
const shortVolSpreadFactor = (0, __1.clampBN)(shortIntensity.mul(numericConstants_1.PERCENTAGE_PRECISION).div(anchor_1.BN.max(numericConstants_1.ONE, volume24H)), clampMin, clampMax);
|
|
245
|
+
const longVolSpread = anchor_1.BN.max(lastOracleConfPct, volSpread.mul(longVolSpreadFactor).div(numericConstants_1.PERCENTAGE_PRECISION));
|
|
246
|
+
const shortVolSpread = anchor_1.BN.max(lastOracleConfPct, volSpread.mul(shortVolSpreadFactor).div(numericConstants_1.PERCENTAGE_PRECISION));
|
|
247
|
+
return [longVolSpread, shortVolSpread];
|
|
248
|
+
}
|
|
249
|
+
exports.calculateVolSpreadBN = calculateVolSpreadBN;
|
|
250
|
+
function calculateSpreadBN(baseSpread, lastOracleReservePriceSpreadPct, lastOracleConfPct, maxSpread, quoteAssetReserve, terminalQuoteAssetReserve, pegMultiplier, baseAssetAmountWithAmm, reservePrice, totalFeeMinusDistributions, baseAssetReserve, minBaseAssetReserve, maxBaseAssetReserve, markStd, oracleStd, longIntensity, shortIntensity, volume24H) {
|
|
251
|
+
const [longVolSpread, shortVolSpread] = calculateVolSpreadBN(lastOracleConfPct, reservePrice, markStd, oracleStd, longIntensity, shortIntensity, volume24H);
|
|
252
|
+
let longSpread = Math.max(baseSpread / 2, longVolSpread.toNumber());
|
|
253
|
+
let shortSpread = Math.max(baseSpread / 2, shortVolSpread.toNumber());
|
|
254
|
+
if (lastOracleReservePriceSpreadPct.gt(numericConstants_1.ZERO)) {
|
|
222
255
|
shortSpread = Math.max(shortSpread, lastOracleReservePriceSpreadPct.abs().toNumber() +
|
|
223
|
-
|
|
256
|
+
shortVolSpread.toNumber());
|
|
224
257
|
}
|
|
225
258
|
else if (lastOracleReservePriceSpreadPct.lt(numericConstants_1.ZERO)) {
|
|
226
259
|
longSpread = Math.max(longSpread, lastOracleReservePriceSpreadPct.abs().toNumber() +
|
|
227
|
-
|
|
260
|
+
longVolSpread.toNumber());
|
|
228
261
|
}
|
|
229
262
|
const maxTargetSpread = Math.max(maxSpread, lastOracleReservePriceSpreadPct.abs().toNumber());
|
|
230
|
-
const
|
|
231
|
-
|
|
232
|
-
const inventorySpreadScale = Math.min(MAX_BID_ASK_INVENTORY_SKEW_FACTOR, 1 + inventoryScale);
|
|
233
|
-
if (netBaseAssetAmount.gt(numericConstants_1.ZERO)) {
|
|
263
|
+
const inventorySpreadScale = calculateInventoryScale(baseAssetAmountWithAmm, baseAssetReserve, minBaseAssetReserve, maxBaseAssetReserve, baseAssetAmountWithAmm.gt(numericConstants_1.ZERO) ? longSpread : shortSpread, maxTargetSpread);
|
|
264
|
+
if (baseAssetAmountWithAmm.gt(numericConstants_1.ZERO)) {
|
|
234
265
|
longSpread *= inventorySpreadScale;
|
|
235
266
|
}
|
|
236
|
-
else if (
|
|
267
|
+
else if (baseAssetAmountWithAmm.lt(numericConstants_1.ZERO)) {
|
|
237
268
|
shortSpread *= inventorySpreadScale;
|
|
238
269
|
}
|
|
239
|
-
const effectiveLeverage = calculateEffectiveLeverage(baseSpread, quoteAssetReserve, terminalQuoteAssetReserve, pegMultiplier,
|
|
270
|
+
const effectiveLeverage = calculateEffectiveLeverage(baseSpread, quoteAssetReserve, terminalQuoteAssetReserve, pegMultiplier, baseAssetAmountWithAmm, reservePrice, totalFeeMinusDistributions);
|
|
271
|
+
const MAX_SPREAD_SCALE = 10;
|
|
240
272
|
if (totalFeeMinusDistributions.gt(numericConstants_1.ZERO)) {
|
|
241
|
-
const spreadScale = Math.min(
|
|
242
|
-
if (
|
|
273
|
+
const spreadScale = Math.min(MAX_SPREAD_SCALE, 1 + effectiveLeverage);
|
|
274
|
+
if (baseAssetAmountWithAmm.gt(numericConstants_1.ZERO)) {
|
|
243
275
|
longSpread *= spreadScale;
|
|
244
276
|
}
|
|
245
277
|
else {
|
|
@@ -247,8 +279,8 @@ function calculateSpreadBN(baseSpread, lastOracleReservePriceSpreadPct, lastOrac
|
|
|
247
279
|
}
|
|
248
280
|
}
|
|
249
281
|
else {
|
|
250
|
-
longSpread *=
|
|
251
|
-
shortSpread *=
|
|
282
|
+
longSpread *= MAX_SPREAD_SCALE;
|
|
283
|
+
shortSpread *= MAX_SPREAD_SCALE;
|
|
252
284
|
}
|
|
253
285
|
const totalSpread = longSpread + shortSpread;
|
|
254
286
|
if (totalSpread > maxTargetSpread) {
|
|
@@ -278,7 +310,9 @@ function calculateSpread(amm, direction, oraclePriceData) {
|
|
|
278
310
|
const confIntervalPct = confInterval
|
|
279
311
|
.mul(numericConstants_1.BID_ASK_SPREAD_PRECISION)
|
|
280
312
|
.div(reservePrice);
|
|
281
|
-
const
|
|
313
|
+
const now = new anchor_1.BN(new Date().getTime() / 1000); //todo
|
|
314
|
+
const liveOracleStd = (0, oracles_1.calculateLiveOracleStd)(amm, oraclePriceData, now);
|
|
315
|
+
const [longSpread, shortSpread] = calculateSpreadBN(amm.baseSpread, targetMarkSpreadPct, confIntervalPct, amm.maxSpread, amm.quoteAssetReserve, amm.terminalQuoteAssetReserve, amm.pegMultiplier, amm.baseAssetAmountWithAmm, reservePrice, amm.totalFeeMinusDistributions, amm.baseAssetReserve, amm.minBaseAssetReserve, amm.maxBaseAssetReserve, amm.markStd, liveOracleStd, amm.longIntensityVolume, amm.shortIntensityVolume, amm.volume24H);
|
|
282
316
|
let spread;
|
|
283
317
|
if ((0, types_1.isVariant)(direction, 'long')) {
|
|
284
318
|
spread = longSpread;
|
package/lib/math/oracles.d.ts
CHANGED
|
@@ -5,3 +5,5 @@ import { BN, PerpMarketAccount } from '../index';
|
|
|
5
5
|
export declare function oraclePriceBands(market: PerpMarketAccount, oraclePriceData: OraclePriceData): [BN, BN];
|
|
6
6
|
export declare function isOracleValid(amm: AMM, oraclePriceData: OraclePriceData, oracleGuardRails: OracleGuardRails, slot: number): boolean;
|
|
7
7
|
export declare function isOracleTooDivergent(amm: AMM, oraclePriceData: OraclePriceData, oracleGuardRails: OracleGuardRails, now: BN): boolean;
|
|
8
|
+
export declare function calculateLiveOracleTwap(amm: AMM, oraclePriceData: OraclePriceData, now: BN): BN;
|
|
9
|
+
export declare function calculateLiveOracleStd(amm: AMM, oraclePriceData: OraclePriceData, now: BN): BN;
|
package/lib/math/oracles.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.isOracleTooDivergent = exports.isOracleValid = exports.oraclePriceBands = void 0;
|
|
3
|
+
exports.calculateLiveOracleStd = exports.calculateLiveOracleTwap = exports.isOracleTooDivergent = exports.isOracleValid = exports.oraclePriceBands = void 0;
|
|
4
4
|
const numericConstants_1 = require("../constants/numericConstants");
|
|
5
5
|
const index_1 = require("../index");
|
|
6
6
|
const assert_1 = require("../assert/assert");
|
|
@@ -52,3 +52,25 @@ function isOracleTooDivergent(amm, oraclePriceData, oracleGuardRails, now) {
|
|
|
52
52
|
return tooDivergent;
|
|
53
53
|
}
|
|
54
54
|
exports.isOracleTooDivergent = isOracleTooDivergent;
|
|
55
|
+
function calculateLiveOracleTwap(amm, oraclePriceData, now) {
|
|
56
|
+
const sinceLastUpdate = now.sub(amm.historicalOracleData.lastOraclePriceTwapTs);
|
|
57
|
+
const sinceStart = index_1.BN.max(numericConstants_1.ZERO, amm.fundingPeriod.sub(sinceLastUpdate));
|
|
58
|
+
const clampRange = amm.historicalOracleData.lastOraclePriceTwap.div(new index_1.BN(3));
|
|
59
|
+
const clampedOraclePrice = index_1.BN.min(amm.historicalOracleData.lastOraclePriceTwap.add(clampRange), index_1.BN.max(oraclePriceData.price, amm.historicalOracleData.lastOraclePriceTwap.sub(clampRange)));
|
|
60
|
+
const newOracleTwap = amm.historicalOracleData.lastOraclePriceTwap
|
|
61
|
+
.mul(sinceStart)
|
|
62
|
+
.add(clampedOraclePrice)
|
|
63
|
+
.mul(sinceLastUpdate)
|
|
64
|
+
.div(sinceStart.add(sinceLastUpdate));
|
|
65
|
+
return newOracleTwap;
|
|
66
|
+
}
|
|
67
|
+
exports.calculateLiveOracleTwap = calculateLiveOracleTwap;
|
|
68
|
+
function calculateLiveOracleStd(amm, oraclePriceData, now) {
|
|
69
|
+
const sinceLastUpdate = now.sub(amm.historicalOracleData.lastOraclePriceTwapTs);
|
|
70
|
+
const sinceStart = index_1.BN.max(numericConstants_1.ZERO, amm.fundingPeriod.sub(sinceLastUpdate));
|
|
71
|
+
const liveOracleTwap = calculateLiveOracleTwap(amm, oraclePriceData, now);
|
|
72
|
+
const priceDeltaVsTwap = oraclePriceData.price.sub(liveOracleTwap).abs();
|
|
73
|
+
const oracleStd = priceDeltaVsTwap.add(amm.oracleStd.mul(sinceStart).div(sinceStart.add(sinceLastUpdate)));
|
|
74
|
+
return oracleStd;
|
|
75
|
+
}
|
|
76
|
+
exports.calculateLiveOracleStd = calculateLiveOracleStd;
|
package/lib/math/orders.d.ts
CHANGED
|
@@ -7,8 +7,7 @@ export declare function isOrderRiskIncreasing(user: User, order: Order): boolean
|
|
|
7
7
|
export declare function isOrderRiskIncreasingInSameDirection(user: User, order: Order): boolean;
|
|
8
8
|
export declare function isOrderReduceOnly(user: User, order: Order): boolean;
|
|
9
9
|
export declare function standardizeBaseAssetAmount(baseAssetAmount: BN, stepSize: BN): BN;
|
|
10
|
-
export declare function getLimitPrice(order: Order, oraclePriceData: OraclePriceData, slot: number): BN;
|
|
11
|
-
export declare function getOptionalLimitPrice(order: Order, oraclePriceData: OraclePriceData, slot: number): BN | undefined;
|
|
10
|
+
export declare function getLimitPrice(order: Order, oraclePriceData: OraclePriceData, slot: number, fallbackPrice?: BN): BN | undefined;
|
|
12
11
|
export declare function hasLimitPrice(order: Order, slot: number): boolean;
|
|
13
12
|
export declare function isFillableByVAMM(order: Order, market: PerpMarketAccount, oraclePriceData: OraclePriceData, slot: number, ts: number): boolean;
|
|
14
13
|
export declare function calculateBaseAssetAmountForAmmToFulfill(order: Order, market: PerpMarketAccount, oraclePriceData: OraclePriceData, slot: number): BN;
|
package/lib/math/orders.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.isTriggered = exports.mustBeTriggered = exports.isLimitOrder = exports.isMarketOrder = exports.isOrderExpired = exports.calculateBaseAssetAmountToFillUpToLimitPrice = exports.calculateBaseAssetAmountForAmmToFulfill = exports.isFillableByVAMM = exports.hasLimitPrice = exports.
|
|
3
|
+
exports.isTriggered = exports.mustBeTriggered = exports.isLimitOrder = exports.isMarketOrder = exports.isOrderExpired = exports.calculateBaseAssetAmountToFillUpToLimitPrice = exports.calculateBaseAssetAmountForAmmToFulfill = exports.isFillableByVAMM = exports.hasLimitPrice = exports.getLimitPrice = exports.standardizeBaseAssetAmount = exports.isOrderReduceOnly = exports.isOrderRiskIncreasingInSameDirection = exports.isOrderRiskIncreasing = void 0;
|
|
4
4
|
const types_1 = require("../types");
|
|
5
5
|
const numericConstants_1 = require("../constants/numericConstants");
|
|
6
6
|
const anchor_1 = require("@project-serum/anchor");
|
|
@@ -79,7 +79,7 @@ function standardizeBaseAssetAmount(baseAssetAmount, stepSize) {
|
|
|
79
79
|
return baseAssetAmount.sub(remainder);
|
|
80
80
|
}
|
|
81
81
|
exports.standardizeBaseAssetAmount = standardizeBaseAssetAmount;
|
|
82
|
-
function getLimitPrice(order, oraclePriceData, slot) {
|
|
82
|
+
function getLimitPrice(order, oraclePriceData, slot, fallbackPrice) {
|
|
83
83
|
let limitPrice;
|
|
84
84
|
if (order.oraclePriceOffset !== 0) {
|
|
85
85
|
limitPrice = oraclePriceData.price.add(new anchor_1.BN(order.oraclePriceOffset));
|
|
@@ -92,14 +92,7 @@ function getLimitPrice(order, oraclePriceData, slot) {
|
|
|
92
92
|
limitPrice = order.price;
|
|
93
93
|
}
|
|
94
94
|
else {
|
|
95
|
-
|
|
96
|
-
const oraclePrice1Pct = oraclePriceData.price.div(new anchor_1.BN(100));
|
|
97
|
-
if ((0, types_1.isVariant)(order.direction, 'long')) {
|
|
98
|
-
limitPrice = oraclePriceData.price.add(oraclePrice1Pct);
|
|
99
|
-
}
|
|
100
|
-
else {
|
|
101
|
-
limitPrice = oraclePriceData.price.sub(oraclePrice1Pct);
|
|
102
|
-
}
|
|
95
|
+
limitPrice = fallbackPrice;
|
|
103
96
|
}
|
|
104
97
|
}
|
|
105
98
|
else {
|
|
@@ -108,15 +101,6 @@ function getLimitPrice(order, oraclePriceData, slot) {
|
|
|
108
101
|
return limitPrice;
|
|
109
102
|
}
|
|
110
103
|
exports.getLimitPrice = getLimitPrice;
|
|
111
|
-
function getOptionalLimitPrice(order, oraclePriceData, slot) {
|
|
112
|
-
if (hasLimitPrice(order, slot)) {
|
|
113
|
-
return getLimitPrice(order, oraclePriceData, slot);
|
|
114
|
-
}
|
|
115
|
-
else {
|
|
116
|
-
return undefined;
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
exports.getOptionalLimitPrice = getOptionalLimitPrice;
|
|
120
104
|
function hasLimitPrice(order, slot) {
|
|
121
105
|
return (order.price.gt(numericConstants_1.ZERO) ||
|
|
122
106
|
order.oraclePriceOffset != 0 ||
|
|
@@ -133,7 +117,7 @@ function calculateBaseAssetAmountForAmmToFulfill(order, market, oraclePriceData,
|
|
|
133
117
|
if (mustBeTriggered(order) && !isTriggered(order)) {
|
|
134
118
|
return numericConstants_1.ZERO;
|
|
135
119
|
}
|
|
136
|
-
const limitPrice =
|
|
120
|
+
const limitPrice = getLimitPrice(order, oraclePriceData, slot);
|
|
137
121
|
let baseAssetAmount;
|
|
138
122
|
const updatedAMM = (0, amm_1.calculateUpdatedAMM)(market.amm, oraclePriceData);
|
|
139
123
|
if (limitPrice !== undefined) {
|
package/lib/math/utils.d.ts
CHANGED
package/lib/math/utils.js
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.squareRootBN = void 0;
|
|
3
|
+
exports.squareRootBN = exports.clampBN = void 0;
|
|
4
4
|
const __1 = require("../");
|
|
5
|
+
function clampBN(x, min, max) {
|
|
6
|
+
return __1.BN.max(min, __1.BN.min(x, max));
|
|
7
|
+
}
|
|
8
|
+
exports.clampBN = clampBN;
|
|
5
9
|
const squareRootBN = (n, closeness = new __1.BN(1)) => {
|
|
6
10
|
// Assuming the sqrt of n as n only
|
|
7
11
|
let x = n;
|
package/lib/types.d.ts
CHANGED
|
@@ -606,6 +606,8 @@ export declare type StateAccount = {
|
|
|
606
606
|
perpFeeStructure: FeeStructure;
|
|
607
607
|
spotFeeStructure: FeeStructure;
|
|
608
608
|
lpCooldownTime: BN;
|
|
609
|
+
initialPctToLiquidate: number;
|
|
610
|
+
liquidationDuration: number;
|
|
609
611
|
};
|
|
610
612
|
export declare type PerpMarketAccount = {
|
|
611
613
|
status: MarketStatus;
|
|
@@ -805,8 +807,8 @@ export declare type PerpPosition = {
|
|
|
805
807
|
settledPnl: BN;
|
|
806
808
|
lpShares: BN;
|
|
807
809
|
remainderBaseAssetAmount: number;
|
|
808
|
-
|
|
809
|
-
|
|
810
|
+
lastBaseAssetAmountPerLp: BN;
|
|
811
|
+
lastQuoteAssetAmountPerLp: BN;
|
|
810
812
|
};
|
|
811
813
|
export declare type UserStatsAccount = {
|
|
812
814
|
numberOfSubAccounts: number;
|
|
@@ -848,6 +850,8 @@ export declare type UserAccount = {
|
|
|
848
850
|
totalWithdraws: BN;
|
|
849
851
|
totalSocialLoss: BN;
|
|
850
852
|
cumulativePerpFunding: BN;
|
|
853
|
+
liquidationMarginFreed: BN;
|
|
854
|
+
liquidationStartSlot: BN;
|
|
851
855
|
};
|
|
852
856
|
export declare type SpotPosition = {
|
|
853
857
|
marketIndex: number;
|
package/lib/user.js
CHANGED
|
@@ -85,8 +85,8 @@ class User {
|
|
|
85
85
|
openAsks: numericConstants_1.ZERO,
|
|
86
86
|
settledPnl: numericConstants_1.ZERO,
|
|
87
87
|
lpShares: numericConstants_1.ZERO,
|
|
88
|
-
|
|
89
|
-
|
|
88
|
+
lastBaseAssetAmountPerLp: numericConstants_1.ZERO,
|
|
89
|
+
lastQuoteAssetAmountPerLp: numericConstants_1.ZERO,
|
|
90
90
|
};
|
|
91
91
|
}
|
|
92
92
|
getClonedPosition(position) {
|
|
@@ -161,11 +161,11 @@ class User {
|
|
|
161
161
|
const market = this.driftClient.getPerpMarketAccount(position.marketIndex);
|
|
162
162
|
const nShares = position.lpShares;
|
|
163
163
|
const deltaBaa = market.amm.baseAssetAmountPerLp
|
|
164
|
-
.sub(position.
|
|
164
|
+
.sub(position.lastBaseAssetAmountPerLp)
|
|
165
165
|
.mul(nShares)
|
|
166
166
|
.div(numericConstants_1.AMM_RESERVE_PRECISION);
|
|
167
167
|
const deltaQaa = market.amm.quoteAssetAmountPerLp
|
|
168
|
-
.sub(position.
|
|
168
|
+
.sub(position.lastQuoteAssetAmountPerLp)
|
|
169
169
|
.mul(nShares)
|
|
170
170
|
.div(numericConstants_1.AMM_RESERVE_PRECISION);
|
|
171
171
|
function sign(v) {
|
|
@@ -364,6 +364,7 @@ class User {
|
|
|
364
364
|
newTotalLiabilityValue =
|
|
365
365
|
newTotalLiabilityValue.add(weightedTokenValue);
|
|
366
366
|
}
|
|
367
|
+
newTotalLiabilityValue = newTotalLiabilityValue.add(new _1.BN(spotPosition.openOrders).mul(numericConstants_1.OPEN_ORDER_MARGIN_REQUIREMENT));
|
|
367
368
|
return newTotalLiabilityValue;
|
|
368
369
|
}, numericConstants_1.ZERO);
|
|
369
370
|
}
|
|
@@ -523,6 +524,9 @@ class User {
|
|
|
523
524
|
baseAssetValue = baseAssetValue
|
|
524
525
|
.mul(marginRatio)
|
|
525
526
|
.div(numericConstants_1.MARGIN_PRECISION);
|
|
527
|
+
if (includeOpenOrders) {
|
|
528
|
+
baseAssetValue = baseAssetValue.add(new _1.BN(perpPosition.openOrders).mul(numericConstants_1.OPEN_ORDER_MARGIN_REQUIREMENT));
|
|
529
|
+
}
|
|
526
530
|
}
|
|
527
531
|
return totalPerpValue.add(baseAssetValue);
|
|
528
532
|
}, numericConstants_1.ZERO);
|
|
@@ -760,8 +764,8 @@ class User {
|
|
|
760
764
|
openAsks: new _1.BN(0),
|
|
761
765
|
settledPnl: numericConstants_1.ZERO,
|
|
762
766
|
lpShares: numericConstants_1.ZERO,
|
|
763
|
-
|
|
764
|
-
|
|
767
|
+
lastBaseAssetAmountPerLp: numericConstants_1.ZERO,
|
|
768
|
+
lastQuoteAssetAmountPerLp: numericConstants_1.ZERO,
|
|
765
769
|
};
|
|
766
770
|
if (proposedBaseAssetAmount.eq(numericConstants_1.ZERO))
|
|
767
771
|
return new _1.BN(-1);
|
package/package.json
CHANGED
|
@@ -317,10 +317,6 @@ export class PollingDriftClientAccountSubscriber
|
|
|
317
317
|
}
|
|
318
318
|
|
|
319
319
|
public async unsubscribe(): Promise<void> {
|
|
320
|
-
if (!this.isSubscribed) {
|
|
321
|
-
return;
|
|
322
|
-
}
|
|
323
|
-
|
|
324
320
|
for (const [_, accountToPoll] of this.accountsToPoll) {
|
|
325
321
|
this.accountLoader.removeAccount(
|
|
326
322
|
accountToPoll.publicKey,
|
|
@@ -132,10 +132,6 @@ export class PollingUserStatsAccountSubscriber
|
|
|
132
132
|
}
|
|
133
133
|
|
|
134
134
|
async unsubscribe(): Promise<void> {
|
|
135
|
-
if (!this.isSubscribed) {
|
|
136
|
-
return;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
135
|
for (const [_, accountToPoll] of this.accountsToPoll) {
|
|
140
136
|
this.accountLoader.removeAccount(
|
|
141
137
|
accountToPoll.publicKey,
|