@drift-labs/sdk 2.40.0-beta.12 → 2.40.0-beta.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/VERSION CHANGED
@@ -1 +1 @@
1
- 2.40.0-beta.12
1
+ 2.40.0-beta.14
@@ -60,6 +60,9 @@ class DLOBSubscriber {
60
60
  throw new Error('Either marketName or marketIndex and marketType must be provided');
61
61
  }
62
62
  }
63
+ if (includeVamm && fallbackL2Generators.length > 0) {
64
+ throw new Error('includeVamm can only be used if fallbackL2Generators is empty');
65
+ }
63
66
  let oraclePriceData;
64
67
  let fallbackBid;
65
68
  let fallbackAsk;
@@ -80,6 +83,7 @@ class DLOBSubscriber {
80
83
  marketAccount: this.driftClient.getPerpMarketAccount(marketIndex),
81
84
  oraclePriceData,
82
85
  numOrders: numVammOrders !== null && numVammOrders !== void 0 ? numVammOrders : depth,
86
+ topOfBookQuoteAmounts: orderBookLevels_1.DEFAULT_TOP_OF_BOOK_QUOTE_AMOUNTS,
83
87
  }),
84
88
  ];
85
89
  }
@@ -26,6 +26,7 @@ export type L3OrderBook = {
26
26
  asks: L3Level[];
27
27
  bids: L3Level[];
28
28
  };
29
+ export declare const DEFAULT_TOP_OF_BOOK_QUOTE_AMOUNTS: any[];
29
30
  /**
30
31
  * Get an {@link Generator<L2Level>} generator from a {@link Generator<DLOBNode>}
31
32
  * @param dlobNodes e.g. {@link DLOB#getMakerLimitAsks} or {@link DLOB#getMakerLimitBids}
@@ -35,11 +36,12 @@ export type L3OrderBook = {
35
36
  export declare function getL2GeneratorFromDLOBNodes(dlobNodes: Generator<DLOBNode>, oraclePriceData: OraclePriceData, slot: number): Generator<L2Level>;
36
37
  export declare function mergeL2LevelGenerators(l2LevelGenerators: Generator<L2Level>[], compare: (a: L2Level, b: L2Level) => boolean): Generator<L2Level>;
37
38
  export declare function createL2Levels(generator: Generator<L2Level>, depth: number): L2Level[];
38
- export declare function getVammL2Generator({ marketAccount, oraclePriceData, numOrders, now, }: {
39
+ export declare function getVammL2Generator({ marketAccount, oraclePriceData, numOrders, now, topOfBookQuoteAmounts, }: {
39
40
  marketAccount: PerpMarketAccount;
40
41
  oraclePriceData: OraclePriceData;
41
42
  numOrders: number;
42
43
  now?: BN;
44
+ topOfBookQuoteAmounts?: BN[];
43
45
  }): L2OrderBookGenerator;
44
46
  export declare function groupL2(l2: L2OrderBook, grouping: BN, depth: number): L2OrderBook;
45
47
  export {};
@@ -1,7 +1,15 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.groupL2 = exports.getVammL2Generator = exports.createL2Levels = exports.mergeL2LevelGenerators = exports.getL2GeneratorFromDLOBNodes = void 0;
3
+ exports.groupL2 = exports.getVammL2Generator = exports.createL2Levels = exports.mergeL2LevelGenerators = exports.getL2GeneratorFromDLOBNodes = exports.DEFAULT_TOP_OF_BOOK_QUOTE_AMOUNTS = void 0;
4
4
  const __1 = require("..");
5
+ const assert_1 = require("../assert/assert");
6
+ exports.DEFAULT_TOP_OF_BOOK_QUOTE_AMOUNTS = [
7
+ new __1.BN(100).mul(__1.QUOTE_PRECISION),
8
+ new __1.BN(500).mul(__1.QUOTE_PRECISION),
9
+ new __1.BN(1000).mul(__1.QUOTE_PRECISION),
10
+ new __1.BN(2000).mul(__1.QUOTE_PRECISION),
11
+ new __1.BN(5000).mul(__1.QUOTE_PRECISION),
12
+ ];
5
13
  /**
6
14
  * Get an {@link Generator<L2Level>} generator from a {@link Generator<DLOBNode>}
7
15
  * @param dlobNodes e.g. {@link DLOB#getMakerLimitAsks} or {@link DLOB#getMakerLimitBids}
@@ -78,13 +86,19 @@ function createL2Levels(generator, depth) {
78
86
  return levels;
79
87
  }
80
88
  exports.createL2Levels = createL2Levels;
81
- function getVammL2Generator({ marketAccount, oraclePriceData, numOrders, now, }) {
89
+ function getVammL2Generator({ marketAccount, oraclePriceData, numOrders, now, topOfBookQuoteAmounts, }) {
90
+ let numBaseOrders = numOrders;
91
+ if (topOfBookQuoteAmounts) {
92
+ numBaseOrders = numOrders - topOfBookQuoteAmounts.length;
93
+ (0, assert_1.assert)(topOfBookQuoteAmounts.length < numOrders);
94
+ }
82
95
  const updatedAmm = (0, __1.calculateUpdatedAMM)(marketAccount.amm, oraclePriceData);
83
96
  const [openBids, openAsks] = (0, __1.calculateMarketOpenBidAsk)(updatedAmm.baseAssetReserve, updatedAmm.minBaseAssetReserve, updatedAmm.maxBaseAssetReserve, updatedAmm.orderStepSize);
84
97
  now = now !== null && now !== void 0 ? now : new __1.BN(Date.now() / 1000);
85
98
  const [bidReserves, askReserves] = (0, __1.calculateSpreadReserves)(updatedAmm, oraclePriceData, now);
86
99
  let numBids = 0;
87
- const baseSize = openBids.div(new __1.BN(numOrders));
100
+ let topOfBookBidSize = __1.ZERO;
101
+ let bidSize = openBids.div(new __1.BN(numBaseOrders));
88
102
  const bidAmm = {
89
103
  baseAssetReserve: bidReserves.baseAssetReserve,
90
104
  quoteAssetReserve: bidReserves.quoteAssetReserve,
@@ -92,22 +106,45 @@ function getVammL2Generator({ marketAccount, oraclePriceData, numOrders, now, })
92
106
  pegMultiplier: updatedAmm.pegMultiplier,
93
107
  };
94
108
  const getL2Bids = function* () {
95
- while (numBids < numOrders && baseSize.gt(__1.ZERO)) {
96
- const [afterSwapQuoteReserves, afterSwapBaseReserves] = (0, __1.calculateAmmReservesAfterSwap)(bidAmm, 'base', baseSize, __1.SwapDirection.ADD);
97
- const quoteSwapped = (0, __1.calculateQuoteAssetAmountSwapped)(bidAmm.quoteAssetReserve.sub(afterSwapQuoteReserves).abs(), bidAmm.pegMultiplier, __1.SwapDirection.ADD);
98
- const price = quoteSwapped.mul(__1.BASE_PRECISION).div(baseSize);
109
+ while (numBids < numOrders && bidSize.gt(__1.ZERO)) {
110
+ let quoteSwapped = __1.ZERO;
111
+ let baseSwapped = __1.ZERO;
112
+ let [afterSwapQuoteReserves, afterSwapBaseReserves] = [__1.ZERO, __1.ZERO];
113
+ if (topOfBookQuoteAmounts && numBids < (topOfBookQuoteAmounts === null || topOfBookQuoteAmounts === void 0 ? void 0 : topOfBookQuoteAmounts.length)) {
114
+ const remainingBaseLiquidity = openBids.sub(topOfBookBidSize);
115
+ quoteSwapped = topOfBookQuoteAmounts[numBids];
116
+ [afterSwapQuoteReserves, afterSwapBaseReserves] =
117
+ (0, __1.calculateAmmReservesAfterSwap)(bidAmm, 'quote', quoteSwapped, __1.SwapDirection.REMOVE);
118
+ baseSwapped = bidAmm.baseAssetReserve.sub(afterSwapBaseReserves).abs();
119
+ if (remainingBaseLiquidity.lt(baseSwapped)) {
120
+ baseSwapped = remainingBaseLiquidity;
121
+ [afterSwapQuoteReserves, afterSwapBaseReserves] =
122
+ (0, __1.calculateAmmReservesAfterSwap)(bidAmm, 'base', baseSwapped, __1.SwapDirection.ADD);
123
+ quoteSwapped = (0, __1.calculateQuoteAssetAmountSwapped)(bidAmm.quoteAssetReserve.sub(afterSwapQuoteReserves).abs(), bidAmm.pegMultiplier, __1.SwapDirection.ADD);
124
+ }
125
+ topOfBookBidSize = topOfBookBidSize.add(baseSwapped);
126
+ bidSize = openBids.sub(topOfBookBidSize).div(new __1.BN(numBaseOrders));
127
+ }
128
+ else {
129
+ baseSwapped = bidSize;
130
+ [afterSwapQuoteReserves, afterSwapBaseReserves] =
131
+ (0, __1.calculateAmmReservesAfterSwap)(bidAmm, 'base', baseSwapped, __1.SwapDirection.ADD);
132
+ quoteSwapped = (0, __1.calculateQuoteAssetAmountSwapped)(bidAmm.quoteAssetReserve.sub(afterSwapQuoteReserves).abs(), bidAmm.pegMultiplier, __1.SwapDirection.ADD);
133
+ }
134
+ const price = quoteSwapped.mul(__1.BASE_PRECISION).div(baseSwapped);
99
135
  bidAmm.baseAssetReserve = afterSwapBaseReserves;
100
136
  bidAmm.quoteAssetReserve = afterSwapQuoteReserves;
101
137
  yield {
102
138
  price,
103
- size: baseSize,
104
- sources: { vamm: baseSize },
139
+ size: baseSwapped,
140
+ sources: { vamm: baseSwapped },
105
141
  };
106
142
  numBids++;
107
143
  }
108
144
  };
109
145
  let numAsks = 0;
110
- const askSize = openAsks.abs().div(new __1.BN(numOrders));
146
+ let topOfBookAskSize = __1.ZERO;
147
+ let askSize = openAsks.abs().div(new __1.BN(numBaseOrders));
111
148
  const askAmm = {
112
149
  baseAssetReserve: askReserves.baseAssetReserve,
113
150
  quoteAssetReserve: askReserves.quoteAssetReserve,
@@ -116,15 +153,42 @@ function getVammL2Generator({ marketAccount, oraclePriceData, numOrders, now, })
116
153
  };
117
154
  const getL2Asks = function* () {
118
155
  while (numAsks < numOrders && askSize.gt(__1.ZERO)) {
119
- const [afterSwapQuoteReserves, afterSwapBaseReserves] = (0, __1.calculateAmmReservesAfterSwap)(askAmm, 'base', askSize, __1.SwapDirection.REMOVE);
120
- const quoteSwapped = (0, __1.calculateQuoteAssetAmountSwapped)(askAmm.quoteAssetReserve.sub(afterSwapQuoteReserves).abs(), askAmm.pegMultiplier, __1.SwapDirection.REMOVE);
121
- const price = quoteSwapped.mul(__1.BASE_PRECISION).div(askSize);
156
+ let quoteSwapped = __1.ZERO;
157
+ let baseSwapped = __1.ZERO;
158
+ let [afterSwapQuoteReserves, afterSwapBaseReserves] = [__1.ZERO, __1.ZERO];
159
+ if (topOfBookQuoteAmounts && numAsks < (topOfBookQuoteAmounts === null || topOfBookQuoteAmounts === void 0 ? void 0 : topOfBookQuoteAmounts.length)) {
160
+ const remainingBaseLiquidity = openAsks
161
+ .mul(new __1.BN(-1))
162
+ .sub(topOfBookAskSize);
163
+ quoteSwapped = topOfBookQuoteAmounts[numAsks];
164
+ [afterSwapQuoteReserves, afterSwapBaseReserves] =
165
+ (0, __1.calculateAmmReservesAfterSwap)(askAmm, 'quote', quoteSwapped, __1.SwapDirection.ADD);
166
+ baseSwapped = askAmm.baseAssetReserve.sub(afterSwapBaseReserves).abs();
167
+ if (remainingBaseLiquidity.lt(baseSwapped)) {
168
+ baseSwapped = remainingBaseLiquidity;
169
+ [afterSwapQuoteReserves, afterSwapBaseReserves] =
170
+ (0, __1.calculateAmmReservesAfterSwap)(bidAmm, 'base', baseSwapped, __1.SwapDirection.REMOVE);
171
+ quoteSwapped = (0, __1.calculateQuoteAssetAmountSwapped)(bidAmm.quoteAssetReserve.sub(afterSwapQuoteReserves).abs(), bidAmm.pegMultiplier, __1.SwapDirection.REMOVE);
172
+ }
173
+ topOfBookAskSize = topOfBookAskSize.add(baseSwapped);
174
+ askSize = openAsks
175
+ .abs()
176
+ .sub(topOfBookAskSize)
177
+ .div(new __1.BN(numBaseOrders));
178
+ }
179
+ else {
180
+ baseSwapped = askSize;
181
+ [afterSwapQuoteReserves, afterSwapBaseReserves] =
182
+ (0, __1.calculateAmmReservesAfterSwap)(askAmm, 'base', askSize, __1.SwapDirection.REMOVE);
183
+ quoteSwapped = (0, __1.calculateQuoteAssetAmountSwapped)(askAmm.quoteAssetReserve.sub(afterSwapQuoteReserves).abs(), askAmm.pegMultiplier, __1.SwapDirection.REMOVE);
184
+ }
185
+ const price = quoteSwapped.mul(__1.BASE_PRECISION).div(baseSwapped);
122
186
  askAmm.baseAssetReserve = afterSwapBaseReserves;
123
187
  askAmm.quoteAssetReserve = afterSwapQuoteReserves;
124
188
  yield {
125
189
  price,
126
- size: askSize,
127
- sources: { vamm: baseSize },
190
+ size: baseSwapped,
191
+ sources: { vamm: baseSwapped },
128
192
  };
129
193
  numAsks++;
130
194
  }
@@ -229,8 +229,8 @@ export declare class JupiterClient {
229
229
  * @param swapMode the swap mode (ExactIn or ExactOut)
230
230
  * @param onlyDirectRoutes whether to only return direct routes
231
231
  */
232
- getQuote({ inputMint, outputMint, amount, maxAccounts, // 52 is an estimated amount with buffer
233
- slippageBps, swapMode, onlyDirectRoutes, }: {
232
+ getQuote({ inputMint, outputMint, amount, maxAccounts, // 50 is an estimated amount with buffer
233
+ slippageBps, swapMode, onlyDirectRoutes, excludeDexes, }: {
234
234
  inputMint: PublicKey;
235
235
  outputMint: PublicKey;
236
236
  amount: BN;
@@ -238,6 +238,7 @@ export declare class JupiterClient {
238
238
  slippageBps?: number;
239
239
  swapMode?: SwapMode;
240
240
  onlyDirectRoutes?: boolean;
241
+ excludeDexes?: string[];
241
242
  }): Promise<QuoteResponse>;
242
243
  /**
243
244
  * Get a swap transaction for quote
@@ -43,8 +43,8 @@ class JupiterClient {
43
43
  * @param swapMode the swap mode (ExactIn or ExactOut)
44
44
  * @param onlyDirectRoutes whether to only return direct routes
45
45
  */
46
- async getQuote({ inputMint, outputMint, amount, maxAccounts = 52, // 52 is an estimated amount with buffer
47
- slippageBps = 50, swapMode = 'ExactIn', onlyDirectRoutes = false, }) {
46
+ async getQuote({ inputMint, outputMint, amount, maxAccounts = 50, // 50 is an estimated amount with buffer
47
+ slippageBps = 50, swapMode = 'ExactIn', onlyDirectRoutes = false, excludeDexes = [], }) {
48
48
  const params = new URLSearchParams({
49
49
  inputMint: inputMint.toString(),
50
50
  outputMint: outputMint.toString(),
@@ -53,6 +53,7 @@ class JupiterClient {
53
53
  swapMode,
54
54
  onlyDirectRoutes: onlyDirectRoutes.toString(),
55
55
  maxAccounts: maxAccounts.toString(),
56
+ excludeDexes: excludeDexes.join(','),
56
57
  }).toString();
57
58
  const quote = await (await (0, node_fetch_1.default)(`${this.url}/v6/quote?${params}`)).json();
58
59
  return quote;
@@ -1,7 +1,18 @@
1
- import { Order } from '../types';
1
+ import { Order, PositionDirection } from '../types';
2
2
  import { BN } from '../.';
3
3
  export declare function isAuctionComplete(order: Order, slot: number): boolean;
4
4
  export declare function isFallbackAvailableLiquiditySource(order: Order, minAuctionDuration: number, slot: number): boolean;
5
5
  export declare function getAuctionPrice(order: Order, slot: number, oraclePrice: BN): BN;
6
6
  export declare function getAuctionPriceForFixedAuction(order: Order, slot: number): BN;
7
7
  export declare function getAuctionPriceForOracleOffsetAuction(order: Order, slot: number, oraclePrice: BN): BN;
8
+ export declare function deriveOracleAuctionParams({ direction, oraclePrice, auctionStartPrice, auctionEndPrice, limitPrice, }: {
9
+ direction: PositionDirection;
10
+ oraclePrice: BN;
11
+ auctionStartPrice: BN;
12
+ auctionEndPrice: BN;
13
+ limitPrice: BN;
14
+ }): {
15
+ auctionStartPrice: BN;
16
+ auctionEndPrice: BN;
17
+ oraclePriceOffset: number;
18
+ };
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getAuctionPriceForOracleOffsetAuction = exports.getAuctionPriceForFixedAuction = exports.getAuctionPrice = exports.isFallbackAvailableLiquiditySource = exports.isAuctionComplete = void 0;
3
+ exports.deriveOracleAuctionParams = exports.getAuctionPriceForOracleOffsetAuction = exports.getAuctionPriceForFixedAuction = exports.getAuctionPrice = exports.isFallbackAvailableLiquiditySource = exports.isAuctionComplete = void 0;
4
4
  const types_1 = require("../types");
5
5
  const _1 = require("../.");
6
6
  function isAuctionComplete(order, slot) {
@@ -89,3 +89,24 @@ function getAuctionPriceForOracleOffsetAuction(order, slot, oraclePrice) {
89
89
  return oraclePrice.add(priceOffset);
90
90
  }
91
91
  exports.getAuctionPriceForOracleOffsetAuction = getAuctionPriceForOracleOffsetAuction;
92
+ function deriveOracleAuctionParams({ direction, oraclePrice, auctionStartPrice, auctionEndPrice, limitPrice, }) {
93
+ let oraclePriceOffset = limitPrice.sub(oraclePrice);
94
+ if (oraclePriceOffset.eq(_1.ZERO)) {
95
+ oraclePriceOffset = (0, types_1.isVariant)(direction, 'long')
96
+ ? auctionEndPrice.sub(oraclePrice).add(_1.ONE)
97
+ : auctionEndPrice.sub(oraclePrice).sub(_1.ONE);
98
+ }
99
+ let oraclePriceOffsetNum;
100
+ try {
101
+ oraclePriceOffsetNum = oraclePriceOffset.toNumber();
102
+ }
103
+ catch (e) {
104
+ oraclePriceOffsetNum = 0;
105
+ }
106
+ return {
107
+ auctionStartPrice: auctionStartPrice.sub(oraclePrice),
108
+ auctionEndPrice: auctionEndPrice.sub(oraclePrice),
109
+ oraclePriceOffset: oraclePriceOffsetNum,
110
+ };
111
+ }
112
+ exports.deriveOracleAuctionParams = deriveOracleAuctionParams;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@drift-labs/sdk",
3
- "version": "2.40.0-beta.12",
3
+ "version": "2.40.0-beta.14",
4
4
  "main": "lib/index.js",
5
5
  "types": "lib/index.d.ts",
6
6
  "author": "crispheaney",
@@ -44,6 +44,7 @@
44
44
  "uuid": "^8.3.2"
45
45
  },
46
46
  "devDependencies": {
47
+ "@types/big.js": "^6.2.0",
47
48
  "@types/chai": "^4.3.1",
48
49
  "@types/jest": "^28.1.3",
49
50
  "@types/mocha": "^9.1.1",
@@ -10,6 +10,7 @@ import {
10
10
  import { DriftClient } from '../driftClient';
11
11
  import { isVariant, MarketType } from '../types';
12
12
  import {
13
+ DEFAULT_TOP_OF_BOOK_QUOTE_AMOUNTS,
13
14
  getVammL2Generator,
14
15
  L2OrderBook,
15
16
  L2OrderBookGenerator,
@@ -102,6 +103,12 @@ export class DLOBSubscriber {
102
103
  }
103
104
  }
104
105
 
106
+ if (includeVamm && fallbackL2Generators.length > 0) {
107
+ throw new Error(
108
+ 'includeVamm can only be used if fallbackL2Generators is empty'
109
+ );
110
+ }
111
+
105
112
  let oraclePriceData;
106
113
  let fallbackBid;
107
114
  let fallbackAsk;
@@ -125,6 +132,7 @@ export class DLOBSubscriber {
125
132
  marketAccount: this.driftClient.getPerpMarketAccount(marketIndex),
126
133
  oraclePriceData,
127
134
  numOrders: numVammOrders ?? depth,
135
+ topOfBookQuoteAmounts: DEFAULT_TOP_OF_BOOK_QUOTE_AMOUNTS,
128
136
  }),
129
137
  ];
130
138
  }
@@ -10,11 +10,13 @@ import {
10
10
  OraclePriceData,
11
11
  PerpMarketAccount,
12
12
  PositionDirection,
13
+ QUOTE_PRECISION,
13
14
  standardizePrice,
14
15
  SwapDirection,
15
16
  ZERO,
16
17
  } from '..';
17
18
  import { PublicKey } from '@solana/web3.js';
19
+ import { assert } from '../assert/assert';
18
20
 
19
21
  type liquiditySource = 'serum' | 'vamm' | 'dlob' | 'phoenix';
20
22
 
@@ -46,6 +48,14 @@ export type L3OrderBook = {
46
48
  bids: L3Level[];
47
49
  };
48
50
 
51
+ export const DEFAULT_TOP_OF_BOOK_QUOTE_AMOUNTS = [
52
+ new BN(100).mul(QUOTE_PRECISION),
53
+ new BN(500).mul(QUOTE_PRECISION),
54
+ new BN(1000).mul(QUOTE_PRECISION),
55
+ new BN(2000).mul(QUOTE_PRECISION),
56
+ new BN(5000).mul(QUOTE_PRECISION),
57
+ ];
58
+
49
59
  /**
50
60
  * Get an {@link Generator<L2Level>} generator from a {@link Generator<DLOBNode>}
51
61
  * @param dlobNodes e.g. {@link DLOB#getMakerLimitAsks} or {@link DLOB#getMakerLimitBids}
@@ -139,12 +149,20 @@ export function getVammL2Generator({
139
149
  oraclePriceData,
140
150
  numOrders,
141
151
  now,
152
+ topOfBookQuoteAmounts,
142
153
  }: {
143
154
  marketAccount: PerpMarketAccount;
144
155
  oraclePriceData: OraclePriceData;
145
156
  numOrders: number;
146
157
  now?: BN;
158
+ topOfBookQuoteAmounts?: BN[];
147
159
  }): L2OrderBookGenerator {
160
+ let numBaseOrders = numOrders;
161
+ if (topOfBookQuoteAmounts) {
162
+ numBaseOrders = numOrders - topOfBookQuoteAmounts.length;
163
+ assert(topOfBookQuoteAmounts.length < numOrders);
164
+ }
165
+
148
166
  const updatedAmm = calculateUpdatedAMM(marketAccount.amm, oraclePriceData);
149
167
 
150
168
  const [openBids, openAsks] = calculateMarketOpenBidAsk(
@@ -162,38 +180,78 @@ export function getVammL2Generator({
162
180
  );
163
181
 
164
182
  let numBids = 0;
165
- const baseSize = openBids.div(new BN(numOrders));
183
+
184
+ let topOfBookBidSize = ZERO;
185
+ let bidSize = openBids.div(new BN(numBaseOrders));
166
186
  const bidAmm = {
167
187
  baseAssetReserve: bidReserves.baseAssetReserve,
168
188
  quoteAssetReserve: bidReserves.quoteAssetReserve,
169
189
  sqrtK: updatedAmm.sqrtK,
170
190
  pegMultiplier: updatedAmm.pegMultiplier,
171
191
  };
192
+
172
193
  const getL2Bids = function* () {
173
- while (numBids < numOrders && baseSize.gt(ZERO)) {
174
- const [afterSwapQuoteReserves, afterSwapBaseReserves] =
175
- calculateAmmReservesAfterSwap(
176
- bidAmm,
177
- 'base',
178
- baseSize,
194
+ while (numBids < numOrders && bidSize.gt(ZERO)) {
195
+ let quoteSwapped = ZERO;
196
+ let baseSwapped = ZERO;
197
+ let [afterSwapQuoteReserves, afterSwapBaseReserves] = [ZERO, ZERO];
198
+
199
+ if (topOfBookQuoteAmounts && numBids < topOfBookQuoteAmounts?.length) {
200
+ const remainingBaseLiquidity = openBids.sub(topOfBookBidSize);
201
+ quoteSwapped = topOfBookQuoteAmounts[numBids];
202
+ [afterSwapQuoteReserves, afterSwapBaseReserves] =
203
+ calculateAmmReservesAfterSwap(
204
+ bidAmm,
205
+ 'quote',
206
+ quoteSwapped,
207
+ SwapDirection.REMOVE
208
+ );
209
+
210
+ baseSwapped = bidAmm.baseAssetReserve.sub(afterSwapBaseReserves).abs();
211
+ if (remainingBaseLiquidity.lt(baseSwapped)) {
212
+ baseSwapped = remainingBaseLiquidity;
213
+ [afterSwapQuoteReserves, afterSwapBaseReserves] =
214
+ calculateAmmReservesAfterSwap(
215
+ bidAmm,
216
+ 'base',
217
+ baseSwapped,
218
+ SwapDirection.ADD
219
+ );
220
+
221
+ quoteSwapped = calculateQuoteAssetAmountSwapped(
222
+ bidAmm.quoteAssetReserve.sub(afterSwapQuoteReserves).abs(),
223
+ bidAmm.pegMultiplier,
224
+ SwapDirection.ADD
225
+ );
226
+ }
227
+ topOfBookBidSize = topOfBookBidSize.add(baseSwapped);
228
+ bidSize = openBids.sub(topOfBookBidSize).div(new BN(numBaseOrders));
229
+ } else {
230
+ baseSwapped = bidSize;
231
+ [afterSwapQuoteReserves, afterSwapBaseReserves] =
232
+ calculateAmmReservesAfterSwap(
233
+ bidAmm,
234
+ 'base',
235
+ baseSwapped,
236
+ SwapDirection.ADD
237
+ );
238
+
239
+ quoteSwapped = calculateQuoteAssetAmountSwapped(
240
+ bidAmm.quoteAssetReserve.sub(afterSwapQuoteReserves).abs(),
241
+ bidAmm.pegMultiplier,
179
242
  SwapDirection.ADD
180
243
  );
244
+ }
181
245
 
182
- const quoteSwapped = calculateQuoteAssetAmountSwapped(
183
- bidAmm.quoteAssetReserve.sub(afterSwapQuoteReserves).abs(),
184
- bidAmm.pegMultiplier,
185
- SwapDirection.ADD
186
- );
187
-
188
- const price = quoteSwapped.mul(BASE_PRECISION).div(baseSize);
246
+ const price = quoteSwapped.mul(BASE_PRECISION).div(baseSwapped);
189
247
 
190
248
  bidAmm.baseAssetReserve = afterSwapBaseReserves;
191
249
  bidAmm.quoteAssetReserve = afterSwapQuoteReserves;
192
250
 
193
251
  yield {
194
252
  price,
195
- size: baseSize,
196
- sources: { vamm: baseSize },
253
+ size: baseSwapped,
254
+ sources: { vamm: baseSwapped },
197
255
  };
198
256
 
199
257
  numBids++;
@@ -201,38 +259,82 @@ export function getVammL2Generator({
201
259
  };
202
260
 
203
261
  let numAsks = 0;
204
- const askSize = openAsks.abs().div(new BN(numOrders));
262
+ let topOfBookAskSize = ZERO;
263
+ let askSize = openAsks.abs().div(new BN(numBaseOrders));
205
264
  const askAmm = {
206
265
  baseAssetReserve: askReserves.baseAssetReserve,
207
266
  quoteAssetReserve: askReserves.quoteAssetReserve,
208
267
  sqrtK: updatedAmm.sqrtK,
209
268
  pegMultiplier: updatedAmm.pegMultiplier,
210
269
  };
270
+
211
271
  const getL2Asks = function* () {
212
272
  while (numAsks < numOrders && askSize.gt(ZERO)) {
213
- const [afterSwapQuoteReserves, afterSwapBaseReserves] =
214
- calculateAmmReservesAfterSwap(
215
- askAmm,
216
- 'base',
217
- askSize,
273
+ let quoteSwapped: BN = ZERO;
274
+ let baseSwapped: BN = ZERO;
275
+ let [afterSwapQuoteReserves, afterSwapBaseReserves] = [ZERO, ZERO];
276
+
277
+ if (topOfBookQuoteAmounts && numAsks < topOfBookQuoteAmounts?.length) {
278
+ const remainingBaseLiquidity = openAsks
279
+ .mul(new BN(-1))
280
+ .sub(topOfBookAskSize);
281
+ quoteSwapped = topOfBookQuoteAmounts[numAsks];
282
+ [afterSwapQuoteReserves, afterSwapBaseReserves] =
283
+ calculateAmmReservesAfterSwap(
284
+ askAmm,
285
+ 'quote',
286
+ quoteSwapped,
287
+ SwapDirection.ADD
288
+ );
289
+
290
+ baseSwapped = askAmm.baseAssetReserve.sub(afterSwapBaseReserves).abs();
291
+ if (remainingBaseLiquidity.lt(baseSwapped)) {
292
+ baseSwapped = remainingBaseLiquidity;
293
+ [afterSwapQuoteReserves, afterSwapBaseReserves] =
294
+ calculateAmmReservesAfterSwap(
295
+ bidAmm,
296
+ 'base',
297
+ baseSwapped,
298
+ SwapDirection.REMOVE
299
+ );
300
+
301
+ quoteSwapped = calculateQuoteAssetAmountSwapped(
302
+ bidAmm.quoteAssetReserve.sub(afterSwapQuoteReserves).abs(),
303
+ bidAmm.pegMultiplier,
304
+ SwapDirection.REMOVE
305
+ );
306
+ }
307
+ topOfBookAskSize = topOfBookAskSize.add(baseSwapped);
308
+ askSize = openAsks
309
+ .abs()
310
+ .sub(topOfBookAskSize)
311
+ .div(new BN(numBaseOrders));
312
+ } else {
313
+ baseSwapped = askSize;
314
+ [afterSwapQuoteReserves, afterSwapBaseReserves] =
315
+ calculateAmmReservesAfterSwap(
316
+ askAmm,
317
+ 'base',
318
+ askSize,
319
+ SwapDirection.REMOVE
320
+ );
321
+
322
+ quoteSwapped = calculateQuoteAssetAmountSwapped(
323
+ askAmm.quoteAssetReserve.sub(afterSwapQuoteReserves).abs(),
324
+ askAmm.pegMultiplier,
218
325
  SwapDirection.REMOVE
219
326
  );
327
+ }
220
328
 
221
- const quoteSwapped = calculateQuoteAssetAmountSwapped(
222
- askAmm.quoteAssetReserve.sub(afterSwapQuoteReserves).abs(),
223
- askAmm.pegMultiplier,
224
- SwapDirection.REMOVE
225
- );
226
-
227
- const price = quoteSwapped.mul(BASE_PRECISION).div(askSize);
329
+ const price = quoteSwapped.mul(BASE_PRECISION).div(baseSwapped);
228
330
 
229
331
  askAmm.baseAssetReserve = afterSwapBaseReserves;
230
332
  askAmm.quoteAssetReserve = afterSwapQuoteReserves;
231
333
 
232
334
  yield {
233
335
  price,
234
- size: askSize,
235
- sources: { vamm: baseSize },
336
+ size: baseSwapped,
337
+ sources: { vamm: baseSwapped },
236
338
  };
237
339
 
238
340
  numAsks++;
@@ -275,10 +275,11 @@ export class JupiterClient {
275
275
  inputMint,
276
276
  outputMint,
277
277
  amount,
278
- maxAccounts = 52, // 52 is an estimated amount with buffer
278
+ maxAccounts = 50, // 50 is an estimated amount with buffer
279
279
  slippageBps = 50,
280
280
  swapMode = 'ExactIn',
281
281
  onlyDirectRoutes = false,
282
+ excludeDexes = [],
282
283
  }: {
283
284
  inputMint: PublicKey;
284
285
  outputMint: PublicKey;
@@ -287,6 +288,7 @@ export class JupiterClient {
287
288
  slippageBps?: number;
288
289
  swapMode?: SwapMode;
289
290
  onlyDirectRoutes?: boolean;
291
+ excludeDexes?: string[];
290
292
  }): Promise<QuoteResponse> {
291
293
  const params = new URLSearchParams({
292
294
  inputMint: inputMint.toString(),
@@ -296,6 +298,7 @@ export class JupiterClient {
296
298
  swapMode,
297
299
  onlyDirectRoutes: onlyDirectRoutes.toString(),
298
300
  maxAccounts: maxAccounts.toString(),
301
+ excludeDexes: excludeDexes.join(','),
299
302
  }).toString();
300
303
  const quote = await (await fetch(`${this.url}/v6/quote?${params}`)).json();
301
304
  return quote;
@@ -1,5 +1,5 @@
1
- import { isOneOfVariant, isVariant, Order } from '../types';
2
- import { BN, ZERO } from '../.';
1
+ import { isOneOfVariant, isVariant, Order, PositionDirection } from '../types';
2
+ import { BN, ONE, ZERO } from '../.';
3
3
 
4
4
  export function isAuctionComplete(order: Order, slot: number): boolean {
5
5
  if (order.auctionDuration === 0) {
@@ -104,3 +104,37 @@ export function getAuctionPriceForOracleOffsetAuction(
104
104
 
105
105
  return oraclePrice.add(priceOffset);
106
106
  }
107
+
108
+ export function deriveOracleAuctionParams({
109
+ direction,
110
+ oraclePrice,
111
+ auctionStartPrice,
112
+ auctionEndPrice,
113
+ limitPrice,
114
+ }: {
115
+ direction: PositionDirection;
116
+ oraclePrice: BN;
117
+ auctionStartPrice: BN;
118
+ auctionEndPrice: BN;
119
+ limitPrice: BN;
120
+ }): { auctionStartPrice: BN; auctionEndPrice: BN; oraclePriceOffset: number } {
121
+ let oraclePriceOffset = limitPrice.sub(oraclePrice);
122
+ if (oraclePriceOffset.eq(ZERO)) {
123
+ oraclePriceOffset = isVariant(direction, 'long')
124
+ ? auctionEndPrice.sub(oraclePrice).add(ONE)
125
+ : auctionEndPrice.sub(oraclePrice).sub(ONE);
126
+ }
127
+
128
+ let oraclePriceOffsetNum;
129
+ try {
130
+ oraclePriceOffsetNum = oraclePriceOffset.toNumber();
131
+ } catch (e) {
132
+ oraclePriceOffsetNum = 0;
133
+ }
134
+
135
+ return {
136
+ auctionStartPrice: auctionStartPrice.sub(oraclePrice),
137
+ auctionEndPrice: auctionEndPrice.sub(oraclePrice),
138
+ oraclePriceOffset: oraclePriceOffsetNum,
139
+ };
140
+ }
package/tests/amm/test.ts CHANGED
@@ -13,6 +13,12 @@ import {
13
13
  calculateAllEstimatedFundingRate,
14
14
  calculateLongShortFundingRateAndLiveTwaps,
15
15
  OraclePriceData,
16
+ getVammL2Generator,
17
+ BASE_PRECISION,
18
+ PerpMarketAccount,
19
+ L2Level,
20
+ calculateUpdatedAMM,
21
+ calculateMarketOpenBidAsk,
16
22
  } from '../../src';
17
23
  import { mockPerpMarkets } from '../dlob/helpers';
18
24
 
@@ -552,4 +558,400 @@ describe('AMM Tests', () => {
552
558
  assert(est1.eq(est2));
553
559
  assert(est2.eq(new BN('-1550')));
554
560
  });
561
+
562
+ it('orderbook L2 gen (no topOfBookQuoteAmounts, 10 numOrders, low liquidity)', async () => {
563
+ const myMockPerpMarkets = _.cloneDeep(mockPerpMarkets);
564
+
565
+ const mockMarket1: PerpMarketAccount = myMockPerpMarkets[0];
566
+ const cc = 38104569;
567
+ mockMarket1.amm.baseAssetReserve = new BN(cc).mul(BASE_PRECISION);
568
+ mockMarket1.amm.maxBaseAssetReserve = mockMarket1.amm.baseAssetReserve.add(
569
+ new BN(1234835)
570
+ );
571
+ mockMarket1.amm.minBaseAssetReserve = mockMarket1.amm.baseAssetReserve.sub(BASE_PRECISION);
572
+ mockMarket1.amm.quoteAssetReserve = new BN(cc).mul(BASE_PRECISION);
573
+ mockMarket1.amm.pegMultiplier = new BN(18.32 * PEG_PRECISION.toNumber());
574
+ mockMarket1.amm.sqrtK = new BN(cc).mul(BASE_PRECISION);
575
+
576
+ const now = new BN(1688881915);
577
+
578
+ const oraclePriceData: OraclePriceData = {
579
+ price: new BN(18.624 * PRICE_PRECISION.toNumber()),
580
+ slot: new BN(0),
581
+ confidence: new BN(1),
582
+ hasSufficientNumberOfDataPoints: true,
583
+ };
584
+ mockMarket1.amm.historicalOracleData.lastOraclePrice = new BN(
585
+ 18.5535 * PRICE_PRECISION.toNumber()
586
+ );
587
+
588
+ const updatedAmm = calculateUpdatedAMM(mockMarket1.amm, oraclePriceData);
589
+
590
+ const [openBids, openAsks] = calculateMarketOpenBidAsk(
591
+ updatedAmm.baseAssetReserve,
592
+ updatedAmm.minBaseAssetReserve,
593
+ updatedAmm.maxBaseAssetReserve,
594
+ updatedAmm.orderStepSize
595
+ );
596
+
597
+ const generator = getVammL2Generator({
598
+ marketAccount: mockMarket1,
599
+ oraclePriceData,
600
+ numOrders: 10,
601
+ now,
602
+ topOfBookQuoteAmounts: [],
603
+ });
604
+
605
+ const bids = Array.from(generator.getL2Bids());
606
+ // console.log(bids);
607
+
608
+ const totalBidSize = bids.reduce((total: BN, order: L2Level) => {
609
+ return total.add(order.size);
610
+ }, ZERO);
611
+
612
+ console.log(
613
+ 'totalBidSize:',
614
+ totalBidSize.toString(),
615
+ 'openBids:',
616
+ openBids.toString()
617
+ );
618
+ assert(totalBidSize.sub(openBids).abs().lt(new BN(10))); // smol err
619
+ assert(totalBidSize.sub(openBids).lt(ZERO)); // under estimation
620
+
621
+ const asks = Array.from(generator.getL2Asks());
622
+ // console.log(asks);
623
+
624
+ const totalAskSize = asks.reduce((total: BN, order: L2Level) => {
625
+ return total.add(order.size);
626
+ }, ZERO);
627
+ console.log(
628
+ 'totalAskSize:',
629
+ totalAskSize.toString(),
630
+ 'openAsks:',
631
+ openAsks.toString()
632
+ );
633
+ assert(totalAskSize.sub(openAsks.abs()).lte(new BN(5))); // only tiny rounding errors
634
+ });
635
+
636
+ it('orderbook L2 gen (no topOfBookQuoteAmounts, 10 numOrders)', async () => {
637
+ const myMockPerpMarkets = _.cloneDeep(mockPerpMarkets);
638
+
639
+ const mockMarket1: PerpMarketAccount = myMockPerpMarkets[0];
640
+ const cc = 38104569;
641
+ mockMarket1.amm.baseAssetReserve = new BN(cc).mul(BASE_PRECISION);
642
+ mockMarket1.amm.maxBaseAssetReserve = mockMarket1.amm.baseAssetReserve.mul(
643
+ new BN(2)
644
+ );
645
+ mockMarket1.amm.minBaseAssetReserve = mockMarket1.amm.baseAssetReserve.div(
646
+ new BN(2)
647
+ );
648
+ mockMarket1.amm.quoteAssetReserve = new BN(cc).mul(BASE_PRECISION);
649
+ mockMarket1.amm.pegMultiplier = new BN(18.32 * PEG_PRECISION.toNumber());
650
+ mockMarket1.amm.sqrtK = new BN(cc).mul(BASE_PRECISION);
651
+
652
+ const now = new BN(1688881915);
653
+
654
+ const oraclePriceData: OraclePriceData = {
655
+ price: new BN(18.624 * PRICE_PRECISION.toNumber()),
656
+ slot: new BN(0),
657
+ confidence: new BN(1),
658
+ hasSufficientNumberOfDataPoints: true,
659
+ };
660
+ mockMarket1.amm.historicalOracleData.lastOraclePrice = new BN(
661
+ 18.5535 * PRICE_PRECISION.toNumber()
662
+ );
663
+
664
+ const updatedAmm = calculateUpdatedAMM(mockMarket1.amm, oraclePriceData);
665
+
666
+ const [openBids, openAsks] = calculateMarketOpenBidAsk(
667
+ updatedAmm.baseAssetReserve,
668
+ updatedAmm.minBaseAssetReserve,
669
+ updatedAmm.maxBaseAssetReserve,
670
+ updatedAmm.orderStepSize
671
+ );
672
+
673
+ const generator = getVammL2Generator({
674
+ marketAccount: mockMarket1,
675
+ oraclePriceData,
676
+ numOrders: 10,
677
+ now,
678
+ topOfBookQuoteAmounts: [],
679
+ });
680
+
681
+ const bids = Array.from(generator.getL2Bids());
682
+ // console.log(bids);
683
+
684
+ const totalBidSize = bids.reduce((total: BN, order: L2Level) => {
685
+ return total.add(order.size);
686
+ }, ZERO);
687
+
688
+ console.log(
689
+ 'totalBidSize:',
690
+ totalBidSize.toString(),
691
+ 'openBids:',
692
+ openBids.toString()
693
+ );
694
+ assert(totalBidSize.eq(openBids));
695
+
696
+ const asks = Array.from(generator.getL2Asks());
697
+ // console.log(asks);
698
+
699
+ const totalAskSize = asks.reduce((total: BN, order: L2Level) => {
700
+ return total.add(order.size);
701
+ }, ZERO);
702
+ console.log(
703
+ 'totalAskSize:',
704
+ totalAskSize.toString(),
705
+ 'openAsks:',
706
+ openAsks.toString()
707
+ );
708
+ assert(totalAskSize.sub(openAsks.abs()).lte(new BN(5))); // only tiny rounding errors
709
+ });
710
+
711
+ it('orderbook L2 gen (4 topOfBookQuoteAmounts, 10 numOrders)', async () => {
712
+ const myMockPerpMarkets = _.cloneDeep(mockPerpMarkets);
713
+
714
+ const mockMarket1: PerpMarketAccount = myMockPerpMarkets[0];
715
+ const cc = 38104569;
716
+ mockMarket1.amm.baseAssetReserve = new BN(cc).mul(BASE_PRECISION);
717
+ mockMarket1.amm.maxBaseAssetReserve = mockMarket1.amm.baseAssetReserve.mul(
718
+ new BN(2)
719
+ );
720
+ mockMarket1.amm.minBaseAssetReserve = mockMarket1.amm.baseAssetReserve.div(
721
+ new BN(2)
722
+ );
723
+ mockMarket1.amm.quoteAssetReserve = new BN(cc).mul(BASE_PRECISION);
724
+ mockMarket1.amm.pegMultiplier = new BN(18.32 * PEG_PRECISION.toNumber());
725
+ mockMarket1.amm.sqrtK = new BN(cc).mul(BASE_PRECISION);
726
+
727
+ const now = new BN(1688881915);
728
+
729
+ const oraclePriceData: OraclePriceData = {
730
+ price: new BN(18.624 * PRICE_PRECISION.toNumber()),
731
+ slot: new BN(0),
732
+ confidence: new BN(1),
733
+ hasSufficientNumberOfDataPoints: true,
734
+ };
735
+ mockMarket1.amm.historicalOracleData.lastOraclePrice = new BN(
736
+ 18.5535 * PRICE_PRECISION.toNumber()
737
+ );
738
+
739
+ const updatedAmm = calculateUpdatedAMM(mockMarket1.amm, oraclePriceData);
740
+
741
+ const [openBids, openAsks] = calculateMarketOpenBidAsk(
742
+ updatedAmm.baseAssetReserve,
743
+ updatedAmm.minBaseAssetReserve,
744
+ updatedAmm.maxBaseAssetReserve,
745
+ updatedAmm.orderStepSize
746
+ );
747
+
748
+ assert(!openAsks.eq(openBids));
749
+
750
+ const generator = getVammL2Generator({
751
+ marketAccount: mockMarket1,
752
+ oraclePriceData,
753
+ numOrders: 10,
754
+ now,
755
+ topOfBookQuoteAmounts: [
756
+ new BN(10).mul(QUOTE_PRECISION),
757
+ new BN(100).mul(QUOTE_PRECISION),
758
+ new BN(1000).mul(QUOTE_PRECISION),
759
+ new BN(10000).mul(QUOTE_PRECISION),
760
+ ],
761
+ });
762
+
763
+ const bids = Array.from(generator.getL2Bids());
764
+ // console.log(bids);
765
+
766
+ const totalBidSize = bids.reduce((total: BN, order: L2Level) => {
767
+ return total.add(order.size);
768
+ }, ZERO);
769
+
770
+ console.log(
771
+ 'totalBidSize:',
772
+ totalBidSize.toString(),
773
+ 'openBids:',
774
+ openBids.toString()
775
+ );
776
+ assert(totalBidSize.eq(openBids));
777
+
778
+ const asks = Array.from(generator.getL2Asks());
779
+ // console.log(asks);
780
+
781
+ const totalAskSize = asks.reduce((total: BN, order: L2Level) => {
782
+ return total.add(order.size);
783
+ }, ZERO);
784
+ console.log(
785
+ 'totalAskSize:',
786
+ totalAskSize.toString(),
787
+ 'openAsks:',
788
+ openAsks.toString()
789
+ );
790
+ assert(totalAskSize.sub(openAsks.abs()).lte(new BN(5))); // only tiny rounding errors
791
+ });
792
+
793
+
794
+ it('orderbook L2 gen (4 topOfBookQuoteAmounts, 10 numOrders, low bid liquidity)', async () => {
795
+ const myMockPerpMarkets = _.cloneDeep(mockPerpMarkets);
796
+
797
+ const mockMarket1: PerpMarketAccount = myMockPerpMarkets[0];
798
+ const cc = 38104569;
799
+ mockMarket1.amm.baseAssetReserve = new BN(cc).mul(BASE_PRECISION);
800
+ mockMarket1.amm.maxBaseAssetReserve = mockMarket1.amm.baseAssetReserve.add(BASE_PRECISION); // only 1 base
801
+ mockMarket1.amm.minBaseAssetReserve = mockMarket1.amm.baseAssetReserve.div(
802
+ new BN(2)
803
+ );
804
+ mockMarket1.amm.quoteAssetReserve = new BN(cc).mul(BASE_PRECISION);
805
+ mockMarket1.amm.pegMultiplier = new BN(18.32 * PEG_PRECISION.toNumber());
806
+ mockMarket1.amm.sqrtK = new BN(cc).mul(BASE_PRECISION);
807
+
808
+ const now = new BN(1688881915);
809
+
810
+ const oraclePriceData: OraclePriceData = {
811
+ price: new BN(18.624 * PRICE_PRECISION.toNumber()),
812
+ slot: new BN(0),
813
+ confidence: new BN(1),
814
+ hasSufficientNumberOfDataPoints: true,
815
+ };
816
+ mockMarket1.amm.historicalOracleData.lastOraclePrice = new BN(
817
+ 18.5535 * PRICE_PRECISION.toNumber()
818
+ );
819
+
820
+ const updatedAmm = calculateUpdatedAMM(mockMarket1.amm, oraclePriceData);
821
+
822
+ const [openBids, openAsks] = calculateMarketOpenBidAsk(
823
+ updatedAmm.baseAssetReserve,
824
+ updatedAmm.minBaseAssetReserve,
825
+ updatedAmm.maxBaseAssetReserve,
826
+ updatedAmm.orderStepSize
827
+ );
828
+
829
+ assert(!openAsks.eq(openBids));
830
+
831
+ const generator = getVammL2Generator({
832
+ marketAccount: mockMarket1,
833
+ oraclePriceData,
834
+ numOrders: 10,
835
+ now,
836
+ topOfBookQuoteAmounts: [
837
+ new BN(10).mul(QUOTE_PRECISION),
838
+ new BN(100).mul(QUOTE_PRECISION),
839
+ new BN(1000).mul(QUOTE_PRECISION),
840
+ new BN(10000).mul(QUOTE_PRECISION),
841
+ ],
842
+ });
843
+
844
+ const bids = Array.from(generator.getL2Bids());
845
+ assert(bids.length == 2);
846
+ console.log(bids[0].size.toString());
847
+ console.log(bids[1].size.toString());
848
+
849
+ const totalBidSize = bids.reduce((total: BN, order: L2Level) => {
850
+ return total.add(order.size);
851
+ }, ZERO);
852
+
853
+ console.log(
854
+ 'totalBidSize:',
855
+ totalBidSize.toString(),
856
+ 'openBids:',
857
+ openBids.toString()
858
+ );
859
+ assert(totalBidSize.eq(openBids));
860
+
861
+ const asks = Array.from(generator.getL2Asks());
862
+ // console.log(asks);
863
+
864
+ const totalAskSize = asks.reduce((total: BN, order: L2Level) => {
865
+ return total.add(order.size);
866
+ }, ZERO);
867
+ console.log(
868
+ 'totalAskSize:',
869
+ totalAskSize.toString(),
870
+ 'openAsks:',
871
+ openAsks.toString()
872
+ );
873
+ assert(totalAskSize.sub(openAsks.abs()).lte(new BN(5))); // only tiny rounding errors
874
+ });
875
+
876
+
877
+ it('orderbook L2 gen (4 topOfBookQuoteAmounts, 10 numOrders, low ask liquidity)', async () => {
878
+ const myMockPerpMarkets = _.cloneDeep(mockPerpMarkets);
879
+
880
+ const mockMarket1: PerpMarketAccount = myMockPerpMarkets[0];
881
+ const cc = 38104569;
882
+ mockMarket1.amm.baseAssetReserve = new BN(cc).mul(BASE_PRECISION);
883
+ mockMarket1.amm.maxBaseAssetReserve = mockMarket1.amm.baseAssetReserve.add(BASE_PRECISION.mul(new BN(1000))); // 1000 base
884
+ mockMarket1.amm.minBaseAssetReserve = mockMarket1.amm.baseAssetReserve.sub(
885
+ BASE_PRECISION.div(new BN(2))
886
+ ); // only .5 base
887
+ mockMarket1.amm.quoteAssetReserve = new BN(cc).mul(BASE_PRECISION);
888
+ mockMarket1.amm.pegMultiplier = new BN(18.32 * PEG_PRECISION.toNumber());
889
+ mockMarket1.amm.sqrtK = new BN(cc).mul(BASE_PRECISION);
890
+
891
+ const now = new BN(1688881915);
892
+
893
+ const oraclePriceData: OraclePriceData = {
894
+ price: new BN(18.624 * PRICE_PRECISION.toNumber()),
895
+ slot: new BN(0),
896
+ confidence: new BN(1),
897
+ hasSufficientNumberOfDataPoints: true,
898
+ };
899
+ mockMarket1.amm.historicalOracleData.lastOraclePrice = new BN(
900
+ 18.5535 * PRICE_PRECISION.toNumber()
901
+ );
902
+
903
+ const updatedAmm = calculateUpdatedAMM(mockMarket1.amm, oraclePriceData);
904
+
905
+ const [openBids, openAsks] = calculateMarketOpenBidAsk(
906
+ updatedAmm.baseAssetReserve,
907
+ updatedAmm.minBaseAssetReserve,
908
+ updatedAmm.maxBaseAssetReserve,
909
+ updatedAmm.orderStepSize
910
+ );
911
+
912
+ assert(!openAsks.eq(openBids));
913
+
914
+ const generator = getVammL2Generator({
915
+ marketAccount: mockMarket1,
916
+ oraclePriceData,
917
+ numOrders: 10,
918
+ now,
919
+ topOfBookQuoteAmounts: [
920
+ new BN(10).mul(QUOTE_PRECISION),
921
+ new BN(100).mul(QUOTE_PRECISION),
922
+ new BN(1000).mul(QUOTE_PRECISION),
923
+ new BN(10000).mul(QUOTE_PRECISION),
924
+ ],
925
+ });
926
+
927
+ const bids = Array.from(generator.getL2Bids());
928
+
929
+ const totalBidSize = bids.reduce((total: BN, order: L2Level) => {
930
+ return total.add(order.size);
931
+ }, ZERO);
932
+
933
+ console.log(
934
+ 'totalBidSize:',
935
+ totalBidSize.toString(),
936
+ 'openBids:',
937
+ openBids.toString()
938
+ );
939
+ assert(totalBidSize.sub(openBids).abs().lt(new BN(5)));
940
+
941
+ const asks = Array.from(generator.getL2Asks());
942
+ // console.log(asks);
943
+
944
+ assert(asks.length == 1);
945
+ console.log(asks[0].size.toString());
946
+ const totalAskSize = asks.reduce((total: BN, order: L2Level) => {
947
+ return total.add(order.size);
948
+ }, ZERO);
949
+ console.log(
950
+ 'totalAskSize:',
951
+ totalAskSize.toString(),
952
+ 'openAsks:',
953
+ openAsks.toString()
954
+ );
955
+ assert(totalAskSize.sub(openAsks.abs()).lte(new BN(5))); // only tiny rounding errors
956
+ });
555
957
  });
@@ -0,0 +1,66 @@
1
+ import {PRICE_PRECISION, BN, deriveOracleAuctionParams} from "../../src";
2
+ import {assert} from "chai";
3
+ import {PositionDirection} from "../../lib";
4
+
5
+ describe('Auction Tests', () => {
6
+
7
+ it('deriveOracleAuctionParams', async () => {
8
+ let oraclePrice = new BN(100).mul(PRICE_PRECISION);
9
+ let auctionStartPrice = new BN(90).mul(PRICE_PRECISION);
10
+ let auctionEndPrice = new BN(110).mul(PRICE_PRECISION);
11
+ let limitPrice = new BN(120).mul(PRICE_PRECISION);
12
+
13
+ let oracleOrderParams = deriveOracleAuctionParams({
14
+ direction: PositionDirection.LONG,
15
+ oraclePrice,
16
+ auctionStartPrice,
17
+ auctionEndPrice,
18
+ limitPrice,
19
+ });
20
+
21
+ assert(oracleOrderParams.auctionStartPrice.eq(new BN(-10).mul(PRICE_PRECISION)));
22
+ assert(oracleOrderParams.auctionEndPrice.eq(new BN(10).mul(PRICE_PRECISION)));
23
+ assert(oracleOrderParams.oraclePriceOffset === 20 * PRICE_PRECISION.toNumber());
24
+
25
+ oracleOrderParams = deriveOracleAuctionParams({
26
+ direction: PositionDirection.LONG,
27
+ oraclePrice,
28
+ auctionStartPrice: oraclePrice,
29
+ auctionEndPrice: oraclePrice,
30
+ limitPrice: oraclePrice,
31
+ });
32
+
33
+ assert(oracleOrderParams.auctionStartPrice.eq(new BN(0)));
34
+ assert(oracleOrderParams.auctionEndPrice.eq(new BN(0)));
35
+ assert(oracleOrderParams.oraclePriceOffset === 1);
36
+
37
+ oraclePrice = new BN(100).mul(PRICE_PRECISION);
38
+ auctionStartPrice = new BN(110).mul(PRICE_PRECISION);
39
+ auctionEndPrice = new BN(90).mul(PRICE_PRECISION);
40
+ limitPrice = new BN(80).mul(PRICE_PRECISION);
41
+
42
+ oracleOrderParams = deriveOracleAuctionParams({
43
+ direction: PositionDirection.SHORT,
44
+ oraclePrice,
45
+ auctionStartPrice,
46
+ auctionEndPrice,
47
+ limitPrice,
48
+ });
49
+
50
+ assert(oracleOrderParams.auctionStartPrice.eq(new BN(10).mul(PRICE_PRECISION)));
51
+ assert(oracleOrderParams.auctionEndPrice.eq(new BN(-10).mul(PRICE_PRECISION)));
52
+ assert(oracleOrderParams.oraclePriceOffset === -20 * PRICE_PRECISION.toNumber());
53
+
54
+ oracleOrderParams = deriveOracleAuctionParams({
55
+ direction: PositionDirection.SHORT,
56
+ oraclePrice,
57
+ auctionStartPrice: oraclePrice,
58
+ auctionEndPrice: oraclePrice,
59
+ limitPrice: oraclePrice,
60
+ });
61
+
62
+ assert(oracleOrderParams.auctionStartPrice.eq(new BN(0)));
63
+ assert(oracleOrderParams.auctionEndPrice.eq(new BN(0)));
64
+ assert(oracleOrderParams.oraclePriceOffset === -1);
65
+ });
66
+ });