@drift-labs/sdk 2.0.15 → 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.
@@ -56,3 +56,4 @@ export declare const QUOTE_SPOT_MARKET_INDEX = 0;
56
56
  export declare const LAMPORTS_PRECISION: BN;
57
57
  export declare const LAMPORTS_EXP: BN;
58
58
  export declare const OPEN_ORDER_MARGIN_REQUIREMENT: BN;
59
+ export declare const DEFAULT_REVENUE_SINCE_LAST_FUNDING_SPREAD_RETREAT: BN;
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.BID_ASK_SPREAD_PRECISION = exports.MARGIN_PRECISION = exports.AMM_TIMES_PEG_TO_QUOTE_PRECISION_RATIO = exports.PRICE_TO_QUOTE_PRECISION = exports.PRICE_DIV_PEG = exports.AMM_TO_QUOTE_PRECISION_RATIO = exports.BASE_PRECISION_EXP = exports.BASE_PRECISION = exports.AMM_RESERVE_PRECISION = exports.PEG_PRECISION = exports.FUNDING_RATE_BUFFER_PRECISION = exports.FUNDING_RATE_PRECISION = exports.PRICE_PRECISION = exports.QUOTE_PRECISION = exports.LIQUIDATION_FEE_PRECISION = exports.SPOT_MARKET_IMF_PRECISION = exports.SPOT_MARKET_IMF_PRECISION_EXP = exports.SPOT_MARKET_BALANCE_PRECISION = exports.SPOT_MARKET_BALANCE_PRECISION_EXP = exports.SPOT_MARKET_WEIGHT_PRECISION = exports.SPOT_MARKET_UTILIZATION_PRECISION = exports.SPOT_MARKET_UTILIZATION_PRECISION_EXP = exports.SPOT_MARKET_CUMULATIVE_INTEREST_PRECISION = exports.SPOT_MARKET_CUMULATIVE_INTEREST_PRECISION_EXP = exports.SPOT_MARKET_RATE_PRECISION = exports.SPOT_MARKET_RATE_PRECISION_EXP = exports.AMM_RESERVE_PRECISION_EXP = exports.PEG_PRECISION_EXP = exports.FUNDING_RATE_PRECISION_EXP = exports.PRICE_PRECISION_EXP = exports.FUNDING_RATE_BUFFER_PRECISION_EXP = exports.QUOTE_PRECISION_EXP = exports.CONCENTRATION_PRECISION = exports.PERCENTAGE_PRECISION = exports.PERCENTAGE_PRECISION_EXP = exports.MAX_LEVERAGE = exports.TEN_MILLION = exports.BN_MAX = exports.TEN_THOUSAND = exports.TEN = exports.NINE = exports.EIGHT = exports.SEVEN = exports.SIX = exports.FIVE = exports.FOUR = exports.THREE = exports.TWO = exports.ONE = exports.ZERO = void 0;
4
- exports.OPEN_ORDER_MARGIN_REQUIREMENT = exports.LAMPORTS_EXP = exports.LAMPORTS_PRECISION = exports.QUOTE_SPOT_MARKET_INDEX = exports.ONE_YEAR = exports.LIQUIDATION_PCT_PRECISION = void 0;
4
+ exports.DEFAULT_REVENUE_SINCE_LAST_FUNDING_SPREAD_RETREAT = exports.OPEN_ORDER_MARGIN_REQUIREMENT = exports.LAMPORTS_EXP = exports.LAMPORTS_PRECISION = exports.QUOTE_SPOT_MARKET_INDEX = exports.ONE_YEAR = exports.LIQUIDATION_PCT_PRECISION = void 0;
5
5
  const web3_js_1 = require("@solana/web3.js");
6
6
  const __1 = require("../");
7
7
  exports.ZERO = new __1.BN(0);
@@ -60,3 +60,4 @@ exports.QUOTE_SPOT_MARKET_INDEX = 0;
60
60
  exports.LAMPORTS_PRECISION = new __1.BN(web3_js_1.LAMPORTS_PER_SOL);
61
61
  exports.LAMPORTS_EXP = new __1.BN(Math.log10(web3_js_1.LAMPORTS_PER_SOL));
62
62
  exports.OPEN_ORDER_MARGIN_REQUIREMENT = exports.QUOTE_PRECISION.div(new __1.BN(100));
63
+ exports.DEFAULT_REVENUE_SINCE_LAST_FUNDING_SPREAD_RETREAT = new __1.BN(-25).mul(exports.QUOTE_PRECISION);
package/lib/math/amm.d.ts CHANGED
@@ -38,12 +38,12 @@ export declare function calculateInventoryScale(baseAssetAmountWithAmm: BN, base
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
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];
42
- export declare function calculateSpread(amm: AMM, direction: PositionDirection, oraclePriceData: OraclePriceData): number;
43
- export declare function calculateSpreadReserves(amm: AMM, direction: PositionDirection, oraclePriceData: OraclePriceData): {
44
- baseAssetReserve: BN;
45
- quoteAssetReserve: BN;
46
- };
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, netRevenueSinceLastFunding: BN, baseAssetReserve: BN, minBaseAssetReserve: BN, maxBaseAssetReserve: BN, markStd: BN, oracleStd: BN, longIntensity: BN, shortIntensity: BN, volume24H: BN): [number, number];
42
+ export declare function calculateSpread(amm: AMM, oraclePriceData: OraclePriceData): [number, number];
43
+ export declare function calculateSpreadReserves(amm: AMM, oraclePriceData: OraclePriceData): {
44
+ baseAssetReserve: any;
45
+ quoteAssetReserve: any;
46
+ }[];
47
47
  /**
48
48
  * Helper function calculating constant product curve output. Agnostic to whether input asset is quote or base
49
49
  *
package/lib/math/amm.js CHANGED
@@ -100,7 +100,10 @@ function calculateUpdatedAMM(amm, oraclePriceData) {
100
100
  exports.calculateUpdatedAMM = calculateUpdatedAMM;
101
101
  function calculateUpdatedAMMSpreadReserves(amm, direction, oraclePriceData) {
102
102
  const newAmm = calculateUpdatedAMM(amm, oraclePriceData);
103
- 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;
104
107
  const result = {
105
108
  baseAssetReserve: dirReserves.baseAssetReserve,
106
109
  quoteAssetReserve: dirReserves.quoteAssetReserve,
@@ -118,8 +121,7 @@ function calculateBidAskPrice(amm, oraclePriceData, withUpdate = true) {
118
121
  else {
119
122
  newAmm = amm;
120
123
  }
121
- const askReserves = calculateSpreadReserves(newAmm, types_1.PositionDirection.LONG, oraclePriceData);
122
- const bidReserves = calculateSpreadReserves(newAmm, types_1.PositionDirection.SHORT, oraclePriceData);
124
+ const [bidReserves, askReserves] = calculateSpreadReserves(newAmm, oraclePriceData);
123
125
  const askPrice = calculatePrice(askReserves.baseAssetReserve, askReserves.quoteAssetReserve, newAmm.pegMultiplier);
124
126
  const bidPrice = calculatePrice(bidReserves.baseAssetReserve, bidReserves.quoteAssetReserve, newAmm.pegMultiplier);
125
127
  return [bidPrice, askPrice];
@@ -236,10 +238,11 @@ function calculateVolSpreadBN(lastOracleConfPct, reservePrice, markStd, oracleSt
236
238
  const marketAvgStdPct = markStd
237
239
  .add(oracleStd)
238
240
  .mul(numericConstants_1.PERCENTAGE_PRECISION)
239
- .div(reservePrice.mul(new anchor_1.BN(2)));
241
+ .div(reservePrice)
242
+ .div(new anchor_1.BN(2));
240
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));
241
245
  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
246
  const longVolSpreadFactor = (0, __1.clampBN)(longIntensity.mul(numericConstants_1.PERCENTAGE_PRECISION).div(anchor_1.BN.max(numericConstants_1.ONE, volume24H)), clampMin, clampMax);
244
247
  const shortVolSpreadFactor = (0, __1.clampBN)(shortIntensity.mul(numericConstants_1.PERCENTAGE_PRECISION).div(anchor_1.BN.max(numericConstants_1.ONE, volume24H)), clampMin, clampMax);
245
248
  const longVolSpread = anchor_1.BN.max(lastOracleConfPct, volSpread.mul(longVolSpreadFactor).div(numericConstants_1.PERCENTAGE_PRECISION));
@@ -247,7 +250,7 @@ function calculateVolSpreadBN(lastOracleConfPct, reservePrice, markStd, oracleSt
247
250
  return [longVolSpread, shortVolSpread];
248
251
  }
249
252
  exports.calculateVolSpreadBN = calculateVolSpreadBN;
250
- function calculateSpreadBN(baseSpread, lastOracleReservePriceSpreadPct, lastOracleConfPct, maxSpread, quoteAssetReserve, terminalQuoteAssetReserve, pegMultiplier, baseAssetAmountWithAmm, reservePrice, totalFeeMinusDistributions, baseAssetReserve, minBaseAssetReserve, maxBaseAssetReserve, markStd, oracleStd, longIntensity, shortIntensity, volume24H) {
253
+ function calculateSpreadBN(baseSpread, lastOracleReservePriceSpreadPct, lastOracleConfPct, maxSpread, quoteAssetReserve, terminalQuoteAssetReserve, pegMultiplier, baseAssetAmountWithAmm, reservePrice, totalFeeMinusDistributions, netRevenueSinceLastFunding, baseAssetReserve, minBaseAssetReserve, maxBaseAssetReserve, markStd, oracleStd, longIntensity, shortIntensity, volume24H) {
251
254
  const [longVolSpread, shortVolSpread] = calculateVolSpreadBN(lastOracleConfPct, reservePrice, markStd, oracleStd, longIntensity, shortIntensity, volume24H);
252
255
  let longSpread = Math.max(baseSpread / 2, longVolSpread.toNumber());
253
256
  let shortSpread = Math.max(baseSpread / 2, shortVolSpread.toNumber());
@@ -267,9 +270,9 @@ function calculateSpreadBN(baseSpread, lastOracleReservePriceSpreadPct, lastOrac
267
270
  else if (baseAssetAmountWithAmm.lt(numericConstants_1.ZERO)) {
268
271
  shortSpread *= inventorySpreadScale;
269
272
  }
270
- const effectiveLeverage = calculateEffectiveLeverage(baseSpread, quoteAssetReserve, terminalQuoteAssetReserve, pegMultiplier, baseAssetAmountWithAmm, reservePrice, totalFeeMinusDistributions);
271
273
  const MAX_SPREAD_SCALE = 10;
272
274
  if (totalFeeMinusDistributions.gt(numericConstants_1.ZERO)) {
275
+ const effectiveLeverage = calculateEffectiveLeverage(baseSpread, quoteAssetReserve, terminalQuoteAssetReserve, pegMultiplier, baseAssetAmountWithAmm, reservePrice, totalFeeMinusDistributions);
273
276
  const spreadScale = Math.min(MAX_SPREAD_SCALE, 1 + effectiveLeverage);
274
277
  if (baseAssetAmountWithAmm.gt(numericConstants_1.ZERO)) {
275
278
  longSpread *= spreadScale;
@@ -282,23 +285,40 @@ function calculateSpreadBN(baseSpread, lastOracleReservePriceSpreadPct, lastOrac
282
285
  longSpread *= MAX_SPREAD_SCALE;
283
286
  shortSpread *= MAX_SPREAD_SCALE;
284
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
+ }
304
+ }
285
305
  const totalSpread = longSpread + shortSpread;
286
306
  if (totalSpread > maxTargetSpread) {
287
307
  if (longSpread > shortSpread) {
288
- longSpread = Math.min(longSpread, maxTargetSpread);
308
+ longSpread = Math.ceil((longSpread * maxTargetSpread) / totalSpread);
289
309
  shortSpread = maxTargetSpread - longSpread;
290
310
  }
291
311
  else {
292
- shortSpread = Math.min(shortSpread, maxTargetSpread);
312
+ shortSpread = Math.ceil((shortSpread * maxTargetSpread) / totalSpread);
293
313
  longSpread = maxTargetSpread - shortSpread;
294
314
  }
295
315
  }
296
316
  return [longSpread, shortSpread];
297
317
  }
298
318
  exports.calculateSpreadBN = calculateSpreadBN;
299
- function calculateSpread(amm, direction, oraclePriceData) {
319
+ function calculateSpread(amm, oraclePriceData) {
300
320
  if (amm.baseSpread == 0 || amm.curveUpdateIntensity == 0) {
301
- return amm.baseSpread / 2;
321
+ return [amm.baseSpread / 2, amm.baseSpread / 2];
302
322
  }
303
323
  const reservePrice = calculatePrice(amm.baseAssetReserve, amm.quoteAssetReserve, amm.pegMultiplier);
304
324
  const targetPrice = (oraclePriceData === null || oraclePriceData === void 0 ? void 0 : oraclePriceData.price) || reservePrice;
@@ -312,38 +332,36 @@ function calculateSpread(amm, direction, oraclePriceData) {
312
332
  .div(reservePrice);
313
333
  const now = new anchor_1.BN(new Date().getTime() / 1000); //todo
314
334
  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);
316
- let spread;
317
- if ((0, types_1.isVariant)(direction, 'long')) {
318
- spread = longSpread;
319
- }
320
- else {
321
- spread = shortSpread;
322
- }
323
- return spread;
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];
324
337
  }
325
338
  exports.calculateSpread = calculateSpread;
326
- function calculateSpreadReserves(amm, direction, oraclePriceData) {
327
- const spread = calculateSpread(amm, direction, oraclePriceData);
328
- 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);
329
356
  return {
330
- baseAssetReserve: amm.baseAssetReserve,
331
- quoteAssetReserve: amm.quoteAssetReserve,
357
+ baseAssetReserve,
358
+ quoteAssetReserve,
332
359
  };
333
360
  }
334
- const quoteAsserReserveDelta = amm.quoteAssetReserve.div(numericConstants_1.BID_ASK_SPREAD_PRECISION.div(new anchor_1.BN(spread / 2)));
335
- let quoteAssetReserve;
336
- if ((0, types_1.isVariant)(direction, 'long')) {
337
- quoteAssetReserve = amm.quoteAssetReserve.add(quoteAsserReserveDelta);
338
- }
339
- else {
340
- quoteAssetReserve = amm.quoteAssetReserve.sub(quoteAsserReserveDelta);
341
- }
342
- const baseAssetReserve = amm.sqrtK.mul(amm.sqrtK).div(quoteAssetReserve);
343
- return {
344
- baseAssetReserve,
345
- quoteAssetReserve,
346
- };
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];
347
365
  }
348
366
  exports.calculateSpreadReserves = calculateSpreadReserves;
349
367
  /**
@@ -410,7 +428,10 @@ function calculateMaxBaseAssetAmountToTrade(amm, limit_price, direction, oracleP
410
428
  .div(limit_price)
411
429
  .div(numericConstants_1.PEG_PRECISION);
412
430
  const newBaseAssetReserve = (0, __1.squareRootBN)(newBaseAssetReserveSquared);
413
- 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;
414
435
  if (newBaseAssetReserve.gt(baseAssetReserveBefore)) {
415
436
  return [
416
437
  newBaseAssetReserve.sub(baseAssetReserveBefore),
@@ -1,4 +1,4 @@
1
1
  /// <reference types="bn.js" />
2
2
  import { BN } from '../';
3
3
  export declare function clampBN(x: BN, min: BN, max: BN): BN;
4
- export declare const squareRootBN: (n: any, closeness?: BN) => any;
4
+ export declare const squareRootBN: (n: any, closeness?: BN) => BN;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@drift-labs/sdk",
3
- "version": "2.0.15",
3
+ "version": "2.0.16",
4
4
  "main": "lib/index.js",
5
5
  "types": "lib/index.d.ts",
6
6
  "author": "crispheaney",
@@ -91,3 +91,7 @@ export const LAMPORTS_PRECISION = new BN(LAMPORTS_PER_SOL);
91
91
  export const LAMPORTS_EXP = new BN(Math.log10(LAMPORTS_PER_SOL));
92
92
 
93
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);
package/src/math/amm.ts CHANGED
@@ -12,6 +12,7 @@ import {
12
12
  PRICE_DIV_PEG,
13
13
  PERCENTAGE_PRECISION,
14
14
  BASE_PRECISION,
15
+ DEFAULT_REVENUE_SINCE_LAST_FUNDING_SPREAD_RETREAT,
15
16
  } from '../constants/numericConstants';
16
17
  import {
17
18
  AMM,
@@ -190,11 +191,15 @@ export function calculateUpdatedAMMSpreadReserves(
190
191
  oraclePriceData: OraclePriceData
191
192
  ): { baseAssetReserve: BN; quoteAssetReserve: BN; sqrtK: BN; newPeg: BN } {
192
193
  const newAmm = calculateUpdatedAMM(amm, oraclePriceData);
193
- const dirReserves = calculateSpreadReserves(
194
+ const [shortReserves, longReserves] = calculateSpreadReserves(
194
195
  newAmm,
195
- direction,
196
196
  oraclePriceData
197
197
  );
198
+
199
+ const dirReserves = isVariant(direction, 'long')
200
+ ? longReserves
201
+ : shortReserves;
202
+
198
203
  const result = {
199
204
  baseAssetReserve: dirReserves.baseAssetReserve,
200
205
  quoteAssetReserve: dirReserves.quoteAssetReserve,
@@ -217,14 +222,8 @@ export function calculateBidAskPrice(
217
222
  newAmm = amm;
218
223
  }
219
224
 
220
- const askReserves = calculateSpreadReserves(
225
+ const [bidReserves, askReserves] = calculateSpreadReserves(
221
226
  newAmm,
222
- PositionDirection.LONG,
223
- oraclePriceData
224
- );
225
- const bidReserves = calculateSpreadReserves(
226
- newAmm,
227
- PositionDirection.SHORT,
228
227
  oraclePriceData
229
228
  );
230
229
 
@@ -431,11 +430,12 @@ export function calculateVolSpreadBN(
431
430
  const marketAvgStdPct = markStd
432
431
  .add(oracleStd)
433
432
  .mul(PERCENTAGE_PRECISION)
434
- .div(reservePrice.mul(new BN(2)));
433
+ .div(reservePrice)
434
+ .div(new BN(2));
435
435
  const volSpread = BN.max(lastOracleConfPct, marketAvgStdPct.div(new BN(2)));
436
436
 
437
+ const clampMin = PERCENTAGE_PRECISION.div(new BN(100));
437
438
  const clampMax = PERCENTAGE_PRECISION.mul(new BN(16)).div(new BN(10));
438
- const clampMin = PERCENTAGE_PRECISION.div(new BN(10));
439
439
 
440
440
  const longVolSpreadFactor = clampBN(
441
441
  longIntensity.mul(PERCENTAGE_PRECISION).div(BN.max(ONE, volume24H)),
@@ -471,6 +471,7 @@ export function calculateSpreadBN(
471
471
  baseAssetAmountWithAmm: BN,
472
472
  reservePrice: BN,
473
473
  totalFeeMinusDistributions: BN,
474
+ netRevenueSinceLastFunding: BN,
474
475
  baseAssetReserve: BN,
475
476
  minBaseAssetReserve: BN,
476
477
  maxBaseAssetReserve: BN,
@@ -527,18 +528,17 @@ export function calculateSpreadBN(
527
528
  shortSpread *= inventorySpreadScale;
528
529
  }
529
530
 
530
- const effectiveLeverage = calculateEffectiveLeverage(
531
- baseSpread,
532
- quoteAssetReserve,
533
- terminalQuoteAssetReserve,
534
- pegMultiplier,
535
- baseAssetAmountWithAmm,
536
- reservePrice,
537
- totalFeeMinusDistributions
538
- );
539
-
540
531
  const MAX_SPREAD_SCALE = 10;
541
532
  if (totalFeeMinusDistributions.gt(ZERO)) {
533
+ const effectiveLeverage = calculateEffectiveLeverage(
534
+ baseSpread,
535
+ quoteAssetReserve,
536
+ terminalQuoteAssetReserve,
537
+ pegMultiplier,
538
+ baseAssetAmountWithAmm,
539
+ reservePrice,
540
+ totalFeeMinusDistributions
541
+ );
542
542
  const spreadScale = Math.min(MAX_SPREAD_SCALE, 1 + effectiveLeverage);
543
543
  if (baseAssetAmountWithAmm.gt(ZERO)) {
544
544
  longSpread *= spreadScale;
@@ -550,13 +550,38 @@ export function calculateSpreadBN(
550
550
  shortSpread *= MAX_SPREAD_SCALE;
551
551
  }
552
552
 
553
+ if (
554
+ netRevenueSinceLastFunding.lt(
555
+ DEFAULT_REVENUE_SINCE_LAST_FUNDING_SPREAD_RETREAT
556
+ )
557
+ ) {
558
+ const retreatAmount = Math.min(
559
+ maxTargetSpread / 10,
560
+ Math.floor(
561
+ (baseSpread * netRevenueSinceLastFunding.abs().toNumber()) /
562
+ DEFAULT_REVENUE_SINCE_LAST_FUNDING_SPREAD_RETREAT.toNumber()
563
+ )
564
+ );
565
+ const halfRetreatAmount = Math.floor(retreatAmount / 2);
566
+ if (baseAssetAmountWithAmm.gt(ZERO)) {
567
+ longSpread += retreatAmount;
568
+ shortSpread += halfRetreatAmount;
569
+ } else if (baseAssetAmountWithAmm.lt(ZERO)) {
570
+ longSpread += halfRetreatAmount;
571
+ shortSpread += retreatAmount;
572
+ } else {
573
+ longSpread += halfRetreatAmount;
574
+ shortSpread += halfRetreatAmount;
575
+ }
576
+ }
577
+
553
578
  const totalSpread = longSpread + shortSpread;
554
579
  if (totalSpread > maxTargetSpread) {
555
580
  if (longSpread > shortSpread) {
556
- longSpread = Math.min(longSpread, maxTargetSpread);
581
+ longSpread = Math.ceil((longSpread * maxTargetSpread) / totalSpread);
557
582
  shortSpread = maxTargetSpread - longSpread;
558
583
  } else {
559
- shortSpread = Math.min(shortSpread, maxTargetSpread);
584
+ shortSpread = Math.ceil((shortSpread * maxTargetSpread) / totalSpread);
560
585
  longSpread = maxTargetSpread - shortSpread;
561
586
  }
562
587
  }
@@ -566,11 +591,10 @@ export function calculateSpreadBN(
566
591
 
567
592
  export function calculateSpread(
568
593
  amm: AMM,
569
- direction: PositionDirection,
570
594
  oraclePriceData: OraclePriceData
571
- ): number {
595
+ ): [number, number] {
572
596
  if (amm.baseSpread == 0 || amm.curveUpdateIntensity == 0) {
573
- return amm.baseSpread / 2;
597
+ return [amm.baseSpread / 2, amm.baseSpread / 2];
574
598
  }
575
599
 
576
600
  const reservePrice = calculatePrice(
@@ -604,6 +628,7 @@ export function calculateSpread(
604
628
  amm.baseAssetAmountWithAmm,
605
629
  reservePrice,
606
630
  amm.totalFeeMinusDistributions,
631
+ amm.netRevenueSinceLastFunding,
607
632
  amm.baseAssetReserve,
608
633
  amm.minBaseAssetReserve,
609
634
  amm.maxBaseAssetReserve,
@@ -614,50 +639,59 @@ export function calculateSpread(
614
639
  amm.volume24H
615
640
  );
616
641
 
617
- let spread: number;
618
-
619
- if (isVariant(direction, 'long')) {
620
- spread = longSpread;
621
- } else {
622
- spread = shortSpread;
623
- }
624
-
625
- return spread;
642
+ return [longSpread, shortSpread];
626
643
  }
627
644
 
628
645
  export function calculateSpreadReserves(
629
646
  amm: AMM,
630
- direction: PositionDirection,
631
647
  oraclePriceData: OraclePriceData
632
- ): {
633
- baseAssetReserve: BN;
634
- quoteAssetReserve: BN;
635
- } {
636
- const spread = calculateSpread(amm, direction, oraclePriceData);
648
+ ) {
649
+ function calculateSpreadReserve(
650
+ spread: number,
651
+ direction: PositionDirection,
652
+ amm: AMM
653
+ ): {
654
+ baseAssetReserve;
655
+ quoteAssetReserve;
656
+ } {
657
+ if (spread === 0) {
658
+ return {
659
+ baseAssetReserve: amm.baseAssetReserve,
660
+ quoteAssetReserve: amm.quoteAssetReserve,
661
+ };
662
+ }
663
+
664
+ const quoteAssetReserveDelta = amm.quoteAssetReserve.div(
665
+ BID_ASK_SPREAD_PRECISION.div(new BN(spread / 2))
666
+ );
667
+
668
+ let quoteAssetReserve;
669
+ if (isVariant(direction, 'long')) {
670
+ quoteAssetReserve = amm.quoteAssetReserve.add(quoteAssetReserveDelta);
671
+ } else {
672
+ quoteAssetReserve = amm.quoteAssetReserve.sub(quoteAssetReserveDelta);
673
+ }
637
674
 
638
- if (spread === 0) {
675
+ const baseAssetReserve = amm.sqrtK.mul(amm.sqrtK).div(quoteAssetReserve);
639
676
  return {
640
- baseAssetReserve: amm.baseAssetReserve,
641
- quoteAssetReserve: amm.quoteAssetReserve,
677
+ baseAssetReserve,
678
+ quoteAssetReserve,
642
679
  };
643
680
  }
644
681
 
645
- const quoteAsserReserveDelta = amm.quoteAssetReserve.div(
646
- BID_ASK_SPREAD_PRECISION.div(new BN(spread / 2))
682
+ const [longSpread, shortSpread] = calculateSpread(amm, oraclePriceData);
683
+ const askReserves = calculateSpreadReserve(
684
+ longSpread,
685
+ PositionDirection.LONG,
686
+ amm
687
+ );
688
+ const bidReserves = calculateSpreadReserve(
689
+ shortSpread,
690
+ PositionDirection.SHORT,
691
+ amm
647
692
  );
648
693
 
649
- let quoteAssetReserve;
650
- if (isVariant(direction, 'long')) {
651
- quoteAssetReserve = amm.quoteAssetReserve.add(quoteAsserReserveDelta);
652
- } else {
653
- quoteAssetReserve = amm.quoteAssetReserve.sub(quoteAsserReserveDelta);
654
- }
655
-
656
- const baseAssetReserve = amm.sqrtK.mul(amm.sqrtK).div(quoteAssetReserve);
657
- return {
658
- baseAssetReserve,
659
- quoteAssetReserve,
660
- };
694
+ return [bidReserves, askReserves];
661
695
  }
662
696
 
663
697
  /**
@@ -749,12 +783,14 @@ export function calculateMaxBaseAssetAmountToTrade(
749
783
  .div(PEG_PRECISION);
750
784
 
751
785
  const newBaseAssetReserve = squareRootBN(newBaseAssetReserveSquared);
752
-
753
- const baseAssetReserveBefore = calculateSpreadReserves(
786
+ const [shortSpreadReserves, longSpreadReserves] = calculateSpreadReserves(
754
787
  amm,
755
- direction,
756
788
  oraclePriceData
757
- ).baseAssetReserve;
789
+ );
790
+
791
+ const baseAssetReserveBefore: BN = isVariant(direction, 'long')
792
+ ? longSpreadReserves.baseAssetReserve
793
+ : shortSpreadReserves.baseAssetReserve;
758
794
 
759
795
  if (newBaseAssetReserve.gt(baseAssetReserveBefore)) {
760
796
  return [
package/src/math/utils.ts CHANGED
@@ -4,7 +4,7 @@ export function clampBN(x: BN, min: BN, max: BN): BN {
4
4
  return BN.max(min, BN.min(x, max));
5
5
  }
6
6
 
7
- export const squareRootBN = (n, closeness = new BN(1)) => {
7
+ export const squareRootBN = (n, closeness = new BN(1)): BN => {
8
8
  // Assuming the sqrt of n as n only
9
9
  let x = n;
10
10
 
@@ -1,77 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.EventList = void 0;
4
- class Node {
5
- constructor(event, next, prev) {
6
- this.event = event;
7
- this.next = next;
8
- this.prev = prev;
9
- }
10
- }
11
- class EventList {
12
- constructor(eventType, maxSize, sortFn, orderDirection) {
13
- this.eventType = eventType;
14
- this.maxSize = maxSize;
15
- this.sortFn = sortFn;
16
- this.orderDirection = orderDirection;
17
- this.size = 0;
18
- }
19
- insert(event) {
20
- this.size++;
21
- const newNode = new Node(event);
22
- if (this.head === undefined) {
23
- this.head = this.tail = newNode;
24
- return;
25
- }
26
- if (this.sortFn(this.head.event, newNode.event) ===
27
- (this.orderDirection === 'asc' ? 'less than' : 'greater than')) {
28
- this.head.prev = newNode;
29
- newNode.next = this.head;
30
- this.head = newNode;
31
- }
32
- else {
33
- let currentNode = this.head;
34
- while (currentNode.next !== undefined &&
35
- this.sortFn(currentNode.next.event, newNode.event) !==
36
- (this.orderDirection === 'asc' ? 'less than' : 'greater than')) {
37
- currentNode = currentNode.next;
38
- }
39
- newNode.next = currentNode.next;
40
- if (currentNode.next !== undefined) {
41
- newNode.next.prev = newNode;
42
- }
43
- currentNode.next = newNode;
44
- newNode.prev = currentNode;
45
- }
46
- if (this.size > this.maxSize) {
47
- this.detach();
48
- }
49
- }
50
- detach() {
51
- const node = this.tail;
52
- if (node.prev !== undefined) {
53
- node.prev.next = node.next;
54
- }
55
- else {
56
- this.head = node.next;
57
- }
58
- if (node.next !== undefined) {
59
- node.next.prev = node.prev;
60
- }
61
- else {
62
- this.tail = node.prev;
63
- }
64
- this.size--;
65
- }
66
- toArray() {
67
- return Array.from(this);
68
- }
69
- *[Symbol.iterator]() {
70
- let node = this.head;
71
- while (node) {
72
- yield node.event;
73
- node = node.next;
74
- }
75
- }
76
- }
77
- exports.EventList = EventList;