@drift-labs/sdk 2.0.14 → 2.0.16

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/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 maxPriceSpread = new anchor_1.BN(amm.maxSpread)
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(maxPriceSpread)) {
34
- const markAdj = targetPriceGap.abs().sub(maxPriceSpread);
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,12 +93,17 @@ 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;
97
101
  function calculateUpdatedAMMSpreadReserves(amm, direction, oraclePriceData) {
98
102
  const newAmm = calculateUpdatedAMM(amm, oraclePriceData);
99
- const dirReserves = calculateSpreadReserves(newAmm, direction, oraclePriceData);
103
+ const [shortReserves, longReserves] = calculateSpreadReserves(newAmm, oraclePriceData);
104
+ const dirReserves = (0, types_1.isVariant)(direction, 'long')
105
+ ? longReserves
106
+ : shortReserves;
100
107
  const result = {
101
108
  baseAssetReserve: dirReserves.baseAssetReserve,
102
109
  quoteAssetReserve: dirReserves.quoteAssetReserve,
@@ -114,8 +121,7 @@ function calculateBidAskPrice(amm, oraclePriceData, withUpdate = true) {
114
121
  else {
115
122
  newAmm = amm;
116
123
  }
117
- const askReserves = calculateSpreadReserves(newAmm, types_1.PositionDirection.LONG, oraclePriceData);
118
- const bidReserves = calculateSpreadReserves(newAmm, types_1.PositionDirection.SHORT, oraclePriceData);
124
+ const [bidReserves, askReserves] = calculateSpreadReserves(newAmm, oraclePriceData);
119
125
  const askPrice = calculatePrice(askReserves.baseAssetReserve, askReserves.quoteAssetReserve, newAmm.pegMultiplier);
120
126
  const bidPrice = calculatePrice(bidReserves.baseAssetReserve, bidReserves.quoteAssetReserve, newAmm.pegMultiplier);
121
127
  return [bidPrice, askPrice];
@@ -168,14 +174,14 @@ exports.calculateAmmReservesAfterSwap = calculateAmmReservesAfterSwap;
168
174
  function calculateMarketOpenBidAsk(baseAssetReserve, minBaseAssetReserve, maxBaseAssetReserve) {
169
175
  // open orders
170
176
  let openAsks;
171
- if (maxBaseAssetReserve > baseAssetReserve) {
177
+ if (maxBaseAssetReserve.gt(baseAssetReserve)) {
172
178
  openAsks = maxBaseAssetReserve.sub(baseAssetReserve).mul(new anchor_1.BN(-1));
173
179
  }
174
180
  else {
175
181
  openAsks = numericConstants_1.ZERO;
176
182
  }
177
183
  let openBids;
178
- if (minBaseAssetReserve < baseAssetReserve) {
184
+ if (minBaseAssetReserve.lt(baseAssetReserve)) {
179
185
  openBids = baseAssetReserve.sub(minBaseAssetReserve);
180
186
  }
181
187
  else {
@@ -184,13 +190,26 @@ function calculateMarketOpenBidAsk(baseAssetReserve, minBaseAssetReserve, maxBas
184
190
  return [openBids, openAsks];
185
191
  }
186
192
  exports.calculateMarketOpenBidAsk = calculateMarketOpenBidAsk;
187
- function calculateInventoryScale(netBaseAssetAmount, baseAssetReserve, minBaseAssetReserve, maxBaseAssetReserve) {
188
- const maxScale = numericConstants_1.BID_ASK_SPREAD_PRECISION.mul(new anchor_1.BN(10));
193
+ function calculateInventoryScale(baseAssetAmountWithAmm, baseAssetReserve, minBaseAssetReserve, maxBaseAssetReserve, directionalSpread, maxSpread) {
194
+ if (baseAssetAmountWithAmm.eq(numericConstants_1.ZERO)) {
195
+ return 0;
196
+ }
197
+ const defaultLargeBidAskFactor = numericConstants_1.BID_ASK_SPREAD_PRECISION.mul(new anchor_1.BN(10));
189
198
  // inventory skew
190
199
  const [openBids, openAsks] = calculateMarketOpenBidAsk(baseAssetReserve, minBaseAssetReserve, maxBaseAssetReserve);
191
200
  const minSideLiquidity = anchor_1.BN.max(new anchor_1.BN(1), anchor_1.BN.min(openBids.abs(), openAsks.abs()));
192
- const inventoryScale = anchor_1.BN.min(maxScale, netBaseAssetAmount.mul(maxScale).div(minSideLiquidity).abs()).toNumber() / numericConstants_1.BID_ASK_SPREAD_PRECISION.toNumber();
193
- return inventoryScale;
201
+ const inventoryScaleMax = anchor_1.BN.max(defaultLargeBidAskFactor, new anchor_1.BN(maxSpread / 2)
202
+ .mul(numericConstants_1.BID_ASK_SPREAD_PRECISION)
203
+ .div(new anchor_1.BN(Math.max(directionalSpread, 1)))).toNumber() / numericConstants_1.BID_ASK_SPREAD_PRECISION.toNumber();
204
+ const inventoryScale = baseAssetAmountWithAmm
205
+ .mul(anchor_1.BN.max(baseAssetAmountWithAmm.abs(), numericConstants_1.BASE_PRECISION))
206
+ .div(numericConstants_1.BASE_PRECISION)
207
+ .mul(defaultLargeBidAskFactor)
208
+ .div(minSideLiquidity)
209
+ .abs()
210
+ .toNumber() / numericConstants_1.BID_ASK_SPREAD_PRECISION.toNumber();
211
+ const inventorySpreadScale = Math.min(inventoryScaleMax, 1 + inventoryScale);
212
+ return inventorySpreadScale;
194
213
  }
195
214
  exports.calculateInventoryScale = calculateInventoryScale;
196
215
  function calculateEffectiveLeverage(baseSpread, quoteAssetReserve, terminalQuoteAssetReserve, pegMultiplier, netBaseAssetAmount, reservePrice, totalFeeMinusDistributions) {
@@ -215,31 +234,47 @@ function calculateMaxSpread(marginRatioInitial) {
215
234
  return maxTargetSpread;
216
235
  }
217
236
  exports.calculateMaxSpread = calculateMaxSpread;
218
- function calculateSpreadBN(baseSpread, lastOracleReservePriceSpreadPct, lastOracleConfPct, maxSpread, quoteAssetReserve, terminalQuoteAssetReserve, pegMultiplier, netBaseAssetAmount, reservePrice, totalFeeMinusDistributions, baseAssetReserve, minBaseAssetReserve, maxBaseAssetReserve) {
219
- let longSpread = baseSpread / 2;
220
- let shortSpread = baseSpread / 2;
221
- if (lastOracleReservePriceSpreadPct.gte(numericConstants_1.ZERO)) {
237
+ function calculateVolSpreadBN(lastOracleConfPct, reservePrice, markStd, oracleStd, longIntensity, shortIntensity, volume24H) {
238
+ const marketAvgStdPct = markStd
239
+ .add(oracleStd)
240
+ .mul(numericConstants_1.PERCENTAGE_PRECISION)
241
+ .div(reservePrice)
242
+ .div(new anchor_1.BN(2));
243
+ const volSpread = anchor_1.BN.max(lastOracleConfPct, marketAvgStdPct.div(new anchor_1.BN(2)));
244
+ const clampMin = numericConstants_1.PERCENTAGE_PRECISION.div(new anchor_1.BN(100));
245
+ const clampMax = numericConstants_1.PERCENTAGE_PRECISION.mul(new anchor_1.BN(16)).div(new anchor_1.BN(10));
246
+ const longVolSpreadFactor = (0, __1.clampBN)(longIntensity.mul(numericConstants_1.PERCENTAGE_PRECISION).div(anchor_1.BN.max(numericConstants_1.ONE, volume24H)), clampMin, clampMax);
247
+ const shortVolSpreadFactor = (0, __1.clampBN)(shortIntensity.mul(numericConstants_1.PERCENTAGE_PRECISION).div(anchor_1.BN.max(numericConstants_1.ONE, volume24H)), clampMin, clampMax);
248
+ const longVolSpread = anchor_1.BN.max(lastOracleConfPct, volSpread.mul(longVolSpreadFactor).div(numericConstants_1.PERCENTAGE_PRECISION));
249
+ const shortVolSpread = anchor_1.BN.max(lastOracleConfPct, volSpread.mul(shortVolSpreadFactor).div(numericConstants_1.PERCENTAGE_PRECISION));
250
+ return [longVolSpread, shortVolSpread];
251
+ }
252
+ exports.calculateVolSpreadBN = calculateVolSpreadBN;
253
+ function calculateSpreadBN(baseSpread, lastOracleReservePriceSpreadPct, lastOracleConfPct, maxSpread, quoteAssetReserve, terminalQuoteAssetReserve, pegMultiplier, baseAssetAmountWithAmm, reservePrice, totalFeeMinusDistributions, netRevenueSinceLastFunding, baseAssetReserve, minBaseAssetReserve, maxBaseAssetReserve, markStd, oracleStd, longIntensity, shortIntensity, volume24H) {
254
+ const [longVolSpread, shortVolSpread] = calculateVolSpreadBN(lastOracleConfPct, reservePrice, markStd, oracleStd, longIntensity, shortIntensity, volume24H);
255
+ let longSpread = Math.max(baseSpread / 2, longVolSpread.toNumber());
256
+ let shortSpread = Math.max(baseSpread / 2, shortVolSpread.toNumber());
257
+ if (lastOracleReservePriceSpreadPct.gt(numericConstants_1.ZERO)) {
222
258
  shortSpread = Math.max(shortSpread, lastOracleReservePriceSpreadPct.abs().toNumber() +
223
- lastOracleConfPct.toNumber());
259
+ shortVolSpread.toNumber());
224
260
  }
225
261
  else if (lastOracleReservePriceSpreadPct.lt(numericConstants_1.ZERO)) {
226
262
  longSpread = Math.max(longSpread, lastOracleReservePriceSpreadPct.abs().toNumber() +
227
- lastOracleConfPct.toNumber());
263
+ longVolSpread.toNumber());
228
264
  }
229
265
  const maxTargetSpread = Math.max(maxSpread, lastOracleReservePriceSpreadPct.abs().toNumber());
230
- const MAX_BID_ASK_INVENTORY_SKEW_FACTOR = 10;
231
- const inventoryScale = calculateInventoryScale(netBaseAssetAmount, baseAssetReserve, minBaseAssetReserve, maxBaseAssetReserve);
232
- const inventorySpreadScale = Math.min(MAX_BID_ASK_INVENTORY_SKEW_FACTOR, 1 + inventoryScale);
233
- if (netBaseAssetAmount.gt(numericConstants_1.ZERO)) {
266
+ const inventorySpreadScale = calculateInventoryScale(baseAssetAmountWithAmm, baseAssetReserve, minBaseAssetReserve, maxBaseAssetReserve, baseAssetAmountWithAmm.gt(numericConstants_1.ZERO) ? longSpread : shortSpread, maxTargetSpread);
267
+ if (baseAssetAmountWithAmm.gt(numericConstants_1.ZERO)) {
234
268
  longSpread *= inventorySpreadScale;
235
269
  }
236
- else if (netBaseAssetAmount.lt(numericConstants_1.ZERO)) {
270
+ else if (baseAssetAmountWithAmm.lt(numericConstants_1.ZERO)) {
237
271
  shortSpread *= inventorySpreadScale;
238
272
  }
239
- const effectiveLeverage = calculateEffectiveLeverage(baseSpread, quoteAssetReserve, terminalQuoteAssetReserve, pegMultiplier, netBaseAssetAmount, reservePrice, totalFeeMinusDistributions);
273
+ const MAX_SPREAD_SCALE = 10;
240
274
  if (totalFeeMinusDistributions.gt(numericConstants_1.ZERO)) {
241
- const spreadScale = Math.min(MAX_BID_ASK_INVENTORY_SKEW_FACTOR, 1 + effectiveLeverage);
242
- if (netBaseAssetAmount.gt(numericConstants_1.ZERO)) {
275
+ const effectiveLeverage = calculateEffectiveLeverage(baseSpread, quoteAssetReserve, terminalQuoteAssetReserve, pegMultiplier, baseAssetAmountWithAmm, reservePrice, totalFeeMinusDistributions);
276
+ const spreadScale = Math.min(MAX_SPREAD_SCALE, 1 + effectiveLeverage);
277
+ if (baseAssetAmountWithAmm.gt(numericConstants_1.ZERO)) {
243
278
  longSpread *= spreadScale;
244
279
  }
245
280
  else {
@@ -247,26 +282,43 @@ function calculateSpreadBN(baseSpread, lastOracleReservePriceSpreadPct, lastOrac
247
282
  }
248
283
  }
249
284
  else {
250
- longSpread *= MAX_BID_ASK_INVENTORY_SKEW_FACTOR;
251
- shortSpread *= MAX_BID_ASK_INVENTORY_SKEW_FACTOR;
285
+ longSpread *= MAX_SPREAD_SCALE;
286
+ shortSpread *= MAX_SPREAD_SCALE;
287
+ }
288
+ if (netRevenueSinceLastFunding.lt(numericConstants_1.DEFAULT_REVENUE_SINCE_LAST_FUNDING_SPREAD_RETREAT)) {
289
+ const retreatAmount = Math.min(maxTargetSpread / 10, Math.floor((baseSpread * netRevenueSinceLastFunding.abs().toNumber()) /
290
+ numericConstants_1.DEFAULT_REVENUE_SINCE_LAST_FUNDING_SPREAD_RETREAT.toNumber()));
291
+ const halfRetreatAmount = Math.floor(retreatAmount / 2);
292
+ if (baseAssetAmountWithAmm.gt(numericConstants_1.ZERO)) {
293
+ longSpread += retreatAmount;
294
+ shortSpread += halfRetreatAmount;
295
+ }
296
+ else if (baseAssetAmountWithAmm.lt(numericConstants_1.ZERO)) {
297
+ longSpread += halfRetreatAmount;
298
+ shortSpread += retreatAmount;
299
+ }
300
+ else {
301
+ longSpread += halfRetreatAmount;
302
+ shortSpread += halfRetreatAmount;
303
+ }
252
304
  }
253
305
  const totalSpread = longSpread + shortSpread;
254
306
  if (totalSpread > maxTargetSpread) {
255
307
  if (longSpread > shortSpread) {
256
- longSpread = Math.min(longSpread, maxTargetSpread);
308
+ longSpread = Math.ceil((longSpread * maxTargetSpread) / totalSpread);
257
309
  shortSpread = maxTargetSpread - longSpread;
258
310
  }
259
311
  else {
260
- shortSpread = Math.min(shortSpread, maxTargetSpread);
312
+ shortSpread = Math.ceil((shortSpread * maxTargetSpread) / totalSpread);
261
313
  longSpread = maxTargetSpread - shortSpread;
262
314
  }
263
315
  }
264
316
  return [longSpread, shortSpread];
265
317
  }
266
318
  exports.calculateSpreadBN = calculateSpreadBN;
267
- function calculateSpread(amm, direction, oraclePriceData) {
319
+ function calculateSpread(amm, oraclePriceData) {
268
320
  if (amm.baseSpread == 0 || amm.curveUpdateIntensity == 0) {
269
- return amm.baseSpread / 2;
321
+ return [amm.baseSpread / 2, amm.baseSpread / 2];
270
322
  }
271
323
  const reservePrice = calculatePrice(amm.baseAssetReserve, amm.quoteAssetReserve, amm.pegMultiplier);
272
324
  const targetPrice = (oraclePriceData === null || oraclePriceData === void 0 ? void 0 : oraclePriceData.price) || reservePrice;
@@ -278,38 +330,38 @@ function calculateSpread(amm, direction, oraclePriceData) {
278
330
  const confIntervalPct = confInterval
279
331
  .mul(numericConstants_1.BID_ASK_SPREAD_PRECISION)
280
332
  .div(reservePrice);
281
- 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);
282
- let spread;
283
- if ((0, types_1.isVariant)(direction, 'long')) {
284
- spread = longSpread;
285
- }
286
- else {
287
- spread = shortSpread;
288
- }
289
- return spread;
333
+ const now = new anchor_1.BN(new Date().getTime() / 1000); //todo
334
+ const liveOracleStd = (0, oracles_1.calculateLiveOracleStd)(amm, oraclePriceData, now);
335
+ const [longSpread, shortSpread] = calculateSpreadBN(amm.baseSpread, targetMarkSpreadPct, confIntervalPct, amm.maxSpread, amm.quoteAssetReserve, amm.terminalQuoteAssetReserve, amm.pegMultiplier, amm.baseAssetAmountWithAmm, reservePrice, amm.totalFeeMinusDistributions, amm.netRevenueSinceLastFunding, amm.baseAssetReserve, amm.minBaseAssetReserve, amm.maxBaseAssetReserve, amm.markStd, liveOracleStd, amm.longIntensityVolume, amm.shortIntensityVolume, amm.volume24H);
336
+ return [longSpread, shortSpread];
290
337
  }
291
338
  exports.calculateSpread = calculateSpread;
292
- function calculateSpreadReserves(amm, direction, oraclePriceData) {
293
- const spread = calculateSpread(amm, direction, oraclePriceData);
294
- if (spread === 0) {
339
+ function calculateSpreadReserves(amm, oraclePriceData) {
340
+ function calculateSpreadReserve(spread, direction, amm) {
341
+ if (spread === 0) {
342
+ return {
343
+ baseAssetReserve: amm.baseAssetReserve,
344
+ quoteAssetReserve: amm.quoteAssetReserve,
345
+ };
346
+ }
347
+ const quoteAssetReserveDelta = amm.quoteAssetReserve.div(numericConstants_1.BID_ASK_SPREAD_PRECISION.div(new anchor_1.BN(spread / 2)));
348
+ let quoteAssetReserve;
349
+ if ((0, types_1.isVariant)(direction, 'long')) {
350
+ quoteAssetReserve = amm.quoteAssetReserve.add(quoteAssetReserveDelta);
351
+ }
352
+ else {
353
+ quoteAssetReserve = amm.quoteAssetReserve.sub(quoteAssetReserveDelta);
354
+ }
355
+ const baseAssetReserve = amm.sqrtK.mul(amm.sqrtK).div(quoteAssetReserve);
295
356
  return {
296
- baseAssetReserve: amm.baseAssetReserve,
297
- quoteAssetReserve: amm.quoteAssetReserve,
357
+ baseAssetReserve,
358
+ quoteAssetReserve,
298
359
  };
299
360
  }
300
- const quoteAsserReserveDelta = amm.quoteAssetReserve.div(numericConstants_1.BID_ASK_SPREAD_PRECISION.div(new anchor_1.BN(spread / 2)));
301
- let quoteAssetReserve;
302
- if ((0, types_1.isVariant)(direction, 'long')) {
303
- quoteAssetReserve = amm.quoteAssetReserve.add(quoteAsserReserveDelta);
304
- }
305
- else {
306
- quoteAssetReserve = amm.quoteAssetReserve.sub(quoteAsserReserveDelta);
307
- }
308
- const baseAssetReserve = amm.sqrtK.mul(amm.sqrtK).div(quoteAssetReserve);
309
- return {
310
- baseAssetReserve,
311
- quoteAssetReserve,
312
- };
361
+ const [longSpread, shortSpread] = calculateSpread(amm, oraclePriceData);
362
+ const askReserves = calculateSpreadReserve(longSpread, types_1.PositionDirection.LONG, amm);
363
+ const bidReserves = calculateSpreadReserve(shortSpread, types_1.PositionDirection.SHORT, amm);
364
+ return [bidReserves, askReserves];
313
365
  }
314
366
  exports.calculateSpreadReserves = calculateSpreadReserves;
315
367
  /**
@@ -376,7 +428,10 @@ function calculateMaxBaseAssetAmountToTrade(amm, limit_price, direction, oracleP
376
428
  .div(limit_price)
377
429
  .div(numericConstants_1.PEG_PRECISION);
378
430
  const newBaseAssetReserve = (0, __1.squareRootBN)(newBaseAssetReserveSquared);
379
- const baseAssetReserveBefore = calculateSpreadReserves(amm, direction, oraclePriceData).baseAssetReserve;
431
+ const [shortSpreadReserves, longSpreadReserves] = calculateSpreadReserves(amm, oraclePriceData);
432
+ const baseAssetReserveBefore = (0, types_1.isVariant)(direction, 'long')
433
+ ? longSpreadReserves.baseAssetReserve
434
+ : shortSpreadReserves.baseAssetReserve;
380
435
  if (newBaseAssetReserve.gt(baseAssetReserveBefore)) {
381
436
  return [
382
437
  newBaseAssetReserve.sub(baseAssetReserveBefore),
@@ -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;
@@ -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;
@@ -1,3 +1,4 @@
1
1
  /// <reference types="bn.js" />
2
2
  import { BN } from '../';
3
- export declare const squareRootBN: (n: any, closeness?: BN) => any;
3
+ export declare function clampBN(x: BN, min: BN, max: BN): BN;
4
+ export declare const squareRootBN: (n: any, closeness?: BN) => BN;
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
- lastNetBaseAssetAmountPerLp: BN;
809
- lastNetQuoteAssetAmountPerLp: BN;
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
- lastNetBaseAssetAmountPerLp: numericConstants_1.ZERO,
89
- lastNetQuoteAssetAmountPerLp: numericConstants_1.ZERO,
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.lastNetBaseAssetAmountPerLp)
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.lastNetQuoteAssetAmountPerLp)
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
- lastNetBaseAssetAmountPerLp: numericConstants_1.ZERO,
764
- lastNetQuoteAssetAmountPerLp: numericConstants_1.ZERO,
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@drift-labs/sdk",
3
- "version": "2.0.14",
3
+ "version": "2.0.16",
4
4
  "main": "lib/index.js",
5
5
  "types": "lib/index.d.ts",
6
6
  "author": "crispheaney",
@@ -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,
@@ -606,6 +606,34 @@ export class AdminClient extends DriftClient {
606
606
  });
607
607
  }
608
608
 
609
+ public async updateInitialPctToLiquidate(
610
+ initialPctToLiquidate: number
611
+ ): Promise<TransactionSignature> {
612
+ return await this.program.rpc.updateInitialPctToLiquidate(
613
+ initialPctToLiquidate,
614
+ {
615
+ accounts: {
616
+ admin: this.wallet.publicKey,
617
+ state: await this.getStatePublicKey(),
618
+ },
619
+ }
620
+ );
621
+ }
622
+
623
+ public async updateLiquidationDuration(
624
+ liquidationDuration: number
625
+ ): Promise<TransactionSignature> {
626
+ return await this.program.rpc.updateLiquidationDuration(
627
+ liquidationDuration,
628
+ {
629
+ accounts: {
630
+ admin: this.wallet.publicKey,
631
+ state: await this.getStatePublicKey(),
632
+ },
633
+ }
634
+ );
635
+ }
636
+
609
637
  public async updateOracleGuardRails(
610
638
  oracleGuardRails: OracleGuardRails
611
639
  ): Promise<TransactionSignature> {
@@ -81,6 +81,7 @@ export const AMM_TIMES_PEG_TO_QUOTE_PRECISION_RATIO =
81
81
  AMM_RESERVE_PRECISION.mul(PEG_PRECISION).div(QUOTE_PRECISION); // 10^9
82
82
  export const MARGIN_PRECISION = TEN_THOUSAND;
83
83
  export const BID_ASK_SPREAD_PRECISION = new BN(1000000); // 10^6
84
+ export const LIQUIDATION_PCT_PRECISION = TEN_THOUSAND;
84
85
 
85
86
  export const ONE_YEAR = new BN(31536000);
86
87
 
@@ -88,3 +89,9 @@ export const QUOTE_SPOT_MARKET_INDEX = 0;
88
89
 
89
90
  export const LAMPORTS_PRECISION = new BN(LAMPORTS_PER_SOL);
90
91
  export const LAMPORTS_EXP = new BN(Math.log10(LAMPORTS_PER_SOL));
92
+
93
+ export const OPEN_ORDER_MARGIN_REQUIREMENT = QUOTE_PRECISION.div(new BN(100));
94
+
95
+ export const DEFAULT_REVENUE_SINCE_LAST_FUNDING_SPREAD_RETREAT = new BN(
96
+ -25
97
+ ).mul(QUOTE_PRECISION);