@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 +1 -1
- package/lib/dlob/DLOBSubscriber.js +4 -0
- package/lib/dlob/orderBookLevels.d.ts +3 -1
- package/lib/dlob/orderBookLevels.js +79 -15
- package/lib/jupiter/jupiterClient.d.ts +3 -2
- package/lib/jupiter/jupiterClient.js +3 -2
- package/lib/math/auction.d.ts +12 -1
- package/lib/math/auction.js +22 -1
- package/package.json +2 -1
- package/src/dlob/DLOBSubscriber.ts +8 -0
- package/src/dlob/orderBookLevels.ts +133 -31
- package/src/jupiter/jupiterClient.ts +4 -1
- package/src/math/auction.ts +36 -2
- package/tests/amm/test.ts +402 -0
- package/tests/auctions/test.ts +66 -0
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
2.40.0-beta.
|
|
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
|
-
|
|
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 &&
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
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:
|
|
104
|
-
sources: { vamm:
|
|
139
|
+
size: baseSwapped,
|
|
140
|
+
sources: { vamm: baseSwapped },
|
|
105
141
|
};
|
|
106
142
|
numBids++;
|
|
107
143
|
}
|
|
108
144
|
};
|
|
109
145
|
let numAsks = 0;
|
|
110
|
-
|
|
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
|
-
|
|
120
|
-
|
|
121
|
-
|
|
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:
|
|
127
|
-
sources: { vamm:
|
|
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, //
|
|
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 =
|
|
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;
|
package/lib/math/auction.d.ts
CHANGED
|
@@ -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
|
+
};
|
package/lib/math/auction.js
CHANGED
|
@@ -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.
|
|
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
|
-
|
|
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 &&
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
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
|
|
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:
|
|
196
|
-
sources: { vamm:
|
|
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
|
-
|
|
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
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
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
|
|
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:
|
|
235
|
-
sources: { vamm:
|
|
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 =
|
|
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;
|
package/src/math/auction.ts
CHANGED
|
@@ -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
|
+
});
|