@drift-labs/sdk 2.40.0-beta.13 → 2.40.0-beta.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/VERSION +1 -1
- package/lib/dlob/DLOBSubscriber.js +4 -0
- package/lib/dlob/orderBookLevels.d.ts +3 -1
- package/lib/dlob/orderBookLevels.js +78 -15
- package/lib/jupiter/jupiterClient.d.ts +1 -1
- package/lib/jupiter/jupiterClient.js +1 -1
- package/package.json +2 -1
- package/src/dlob/DLOBSubscriber.ts +8 -0
- package/src/dlob/orderBookLevels.ts +132 -31
- package/src/jupiter/jupiterClient.ts +1 -1
- package/tests/amm/test.ts +402 -0
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
2.40.0-beta.
|
|
1
|
+
2.40.0-beta.15
|
|
@@ -75,11 +75,15 @@ class DLOBSubscriber {
|
|
|
75
75
|
this.driftClient.getOracleDataForSpotMarket(marketIndex);
|
|
76
76
|
}
|
|
77
77
|
if (isPerp && includeVamm) {
|
|
78
|
+
if (fallbackL2Generators.length > 0) {
|
|
79
|
+
throw new Error('includeVamm can only be used if fallbackL2Generators is empty');
|
|
80
|
+
}
|
|
78
81
|
fallbackL2Generators = [
|
|
79
82
|
(0, orderBookLevels_1.getVammL2Generator)({
|
|
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,14 @@
|
|
|
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(500).mul(__1.QUOTE_PRECISION),
|
|
8
|
+
new __1.BN(1000).mul(__1.QUOTE_PRECISION),
|
|
9
|
+
new __1.BN(2000).mul(__1.QUOTE_PRECISION),
|
|
10
|
+
new __1.BN(5000).mul(__1.QUOTE_PRECISION),
|
|
11
|
+
];
|
|
5
12
|
/**
|
|
6
13
|
* Get an {@link Generator<L2Level>} generator from a {@link Generator<DLOBNode>}
|
|
7
14
|
* @param dlobNodes e.g. {@link DLOB#getMakerLimitAsks} or {@link DLOB#getMakerLimitBids}
|
|
@@ -78,13 +85,19 @@ function createL2Levels(generator, depth) {
|
|
|
78
85
|
return levels;
|
|
79
86
|
}
|
|
80
87
|
exports.createL2Levels = createL2Levels;
|
|
81
|
-
function getVammL2Generator({ marketAccount, oraclePriceData, numOrders, now, }) {
|
|
88
|
+
function getVammL2Generator({ marketAccount, oraclePriceData, numOrders, now, topOfBookQuoteAmounts, }) {
|
|
89
|
+
let numBaseOrders = numOrders;
|
|
90
|
+
if (topOfBookQuoteAmounts) {
|
|
91
|
+
numBaseOrders = numOrders - topOfBookQuoteAmounts.length;
|
|
92
|
+
(0, assert_1.assert)(topOfBookQuoteAmounts.length < numOrders);
|
|
93
|
+
}
|
|
82
94
|
const updatedAmm = (0, __1.calculateUpdatedAMM)(marketAccount.amm, oraclePriceData);
|
|
83
95
|
const [openBids, openAsks] = (0, __1.calculateMarketOpenBidAsk)(updatedAmm.baseAssetReserve, updatedAmm.minBaseAssetReserve, updatedAmm.maxBaseAssetReserve, updatedAmm.orderStepSize);
|
|
84
96
|
now = now !== null && now !== void 0 ? now : new __1.BN(Date.now() / 1000);
|
|
85
97
|
const [bidReserves, askReserves] = (0, __1.calculateSpreadReserves)(updatedAmm, oraclePriceData, now);
|
|
86
98
|
let numBids = 0;
|
|
87
|
-
|
|
99
|
+
let topOfBookBidSize = __1.ZERO;
|
|
100
|
+
let bidSize = openBids.div(new __1.BN(numBaseOrders));
|
|
88
101
|
const bidAmm = {
|
|
89
102
|
baseAssetReserve: bidReserves.baseAssetReserve,
|
|
90
103
|
quoteAssetReserve: bidReserves.quoteAssetReserve,
|
|
@@ -92,22 +105,45 @@ function getVammL2Generator({ marketAccount, oraclePriceData, numOrders, now, })
|
|
|
92
105
|
pegMultiplier: updatedAmm.pegMultiplier,
|
|
93
106
|
};
|
|
94
107
|
const getL2Bids = function* () {
|
|
95
|
-
while (numBids < numOrders &&
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
108
|
+
while (numBids < numOrders && bidSize.gt(__1.ZERO)) {
|
|
109
|
+
let quoteSwapped = __1.ZERO;
|
|
110
|
+
let baseSwapped = __1.ZERO;
|
|
111
|
+
let [afterSwapQuoteReserves, afterSwapBaseReserves] = [__1.ZERO, __1.ZERO];
|
|
112
|
+
if (topOfBookQuoteAmounts && numBids < (topOfBookQuoteAmounts === null || topOfBookQuoteAmounts === void 0 ? void 0 : topOfBookQuoteAmounts.length)) {
|
|
113
|
+
const remainingBaseLiquidity = openBids.sub(topOfBookBidSize);
|
|
114
|
+
quoteSwapped = topOfBookQuoteAmounts[numBids];
|
|
115
|
+
[afterSwapQuoteReserves, afterSwapBaseReserves] =
|
|
116
|
+
(0, __1.calculateAmmReservesAfterSwap)(bidAmm, 'quote', quoteSwapped, __1.SwapDirection.REMOVE);
|
|
117
|
+
baseSwapped = bidAmm.baseAssetReserve.sub(afterSwapBaseReserves).abs();
|
|
118
|
+
if (remainingBaseLiquidity.lt(baseSwapped)) {
|
|
119
|
+
baseSwapped = remainingBaseLiquidity;
|
|
120
|
+
[afterSwapQuoteReserves, afterSwapBaseReserves] =
|
|
121
|
+
(0, __1.calculateAmmReservesAfterSwap)(bidAmm, 'base', baseSwapped, __1.SwapDirection.ADD);
|
|
122
|
+
quoteSwapped = (0, __1.calculateQuoteAssetAmountSwapped)(bidAmm.quoteAssetReserve.sub(afterSwapQuoteReserves).abs(), bidAmm.pegMultiplier, __1.SwapDirection.ADD);
|
|
123
|
+
}
|
|
124
|
+
topOfBookBidSize = topOfBookBidSize.add(baseSwapped);
|
|
125
|
+
bidSize = openBids.sub(topOfBookBidSize).div(new __1.BN(numBaseOrders));
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
baseSwapped = bidSize;
|
|
129
|
+
[afterSwapQuoteReserves, afterSwapBaseReserves] =
|
|
130
|
+
(0, __1.calculateAmmReservesAfterSwap)(bidAmm, 'base', baseSwapped, __1.SwapDirection.ADD);
|
|
131
|
+
quoteSwapped = (0, __1.calculateQuoteAssetAmountSwapped)(bidAmm.quoteAssetReserve.sub(afterSwapQuoteReserves).abs(), bidAmm.pegMultiplier, __1.SwapDirection.ADD);
|
|
132
|
+
}
|
|
133
|
+
const price = quoteSwapped.mul(__1.BASE_PRECISION).div(baseSwapped);
|
|
99
134
|
bidAmm.baseAssetReserve = afterSwapBaseReserves;
|
|
100
135
|
bidAmm.quoteAssetReserve = afterSwapQuoteReserves;
|
|
101
136
|
yield {
|
|
102
137
|
price,
|
|
103
|
-
size:
|
|
104
|
-
sources: { vamm:
|
|
138
|
+
size: baseSwapped,
|
|
139
|
+
sources: { vamm: baseSwapped },
|
|
105
140
|
};
|
|
106
141
|
numBids++;
|
|
107
142
|
}
|
|
108
143
|
};
|
|
109
144
|
let numAsks = 0;
|
|
110
|
-
|
|
145
|
+
let topOfBookAskSize = __1.ZERO;
|
|
146
|
+
let askSize = openAsks.abs().div(new __1.BN(numBaseOrders));
|
|
111
147
|
const askAmm = {
|
|
112
148
|
baseAssetReserve: askReserves.baseAssetReserve,
|
|
113
149
|
quoteAssetReserve: askReserves.quoteAssetReserve,
|
|
@@ -116,15 +152,42 @@ function getVammL2Generator({ marketAccount, oraclePriceData, numOrders, now, })
|
|
|
116
152
|
};
|
|
117
153
|
const getL2Asks = function* () {
|
|
118
154
|
while (numAsks < numOrders && askSize.gt(__1.ZERO)) {
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
155
|
+
let quoteSwapped = __1.ZERO;
|
|
156
|
+
let baseSwapped = __1.ZERO;
|
|
157
|
+
let [afterSwapQuoteReserves, afterSwapBaseReserves] = [__1.ZERO, __1.ZERO];
|
|
158
|
+
if (topOfBookQuoteAmounts && numAsks < (topOfBookQuoteAmounts === null || topOfBookQuoteAmounts === void 0 ? void 0 : topOfBookQuoteAmounts.length)) {
|
|
159
|
+
const remainingBaseLiquidity = openAsks
|
|
160
|
+
.mul(new __1.BN(-1))
|
|
161
|
+
.sub(topOfBookAskSize);
|
|
162
|
+
quoteSwapped = topOfBookQuoteAmounts[numAsks];
|
|
163
|
+
[afterSwapQuoteReserves, afterSwapBaseReserves] =
|
|
164
|
+
(0, __1.calculateAmmReservesAfterSwap)(askAmm, 'quote', quoteSwapped, __1.SwapDirection.ADD);
|
|
165
|
+
baseSwapped = askAmm.baseAssetReserve.sub(afterSwapBaseReserves).abs();
|
|
166
|
+
if (remainingBaseLiquidity.lt(baseSwapped)) {
|
|
167
|
+
baseSwapped = remainingBaseLiquidity;
|
|
168
|
+
[afterSwapQuoteReserves, afterSwapBaseReserves] =
|
|
169
|
+
(0, __1.calculateAmmReservesAfterSwap)(bidAmm, 'base', baseSwapped, __1.SwapDirection.REMOVE);
|
|
170
|
+
quoteSwapped = (0, __1.calculateQuoteAssetAmountSwapped)(bidAmm.quoteAssetReserve.sub(afterSwapQuoteReserves).abs(), bidAmm.pegMultiplier, __1.SwapDirection.REMOVE);
|
|
171
|
+
}
|
|
172
|
+
topOfBookAskSize = topOfBookAskSize.add(baseSwapped);
|
|
173
|
+
askSize = openAsks
|
|
174
|
+
.abs()
|
|
175
|
+
.sub(topOfBookAskSize)
|
|
176
|
+
.div(new __1.BN(numBaseOrders));
|
|
177
|
+
}
|
|
178
|
+
else {
|
|
179
|
+
baseSwapped = askSize;
|
|
180
|
+
[afterSwapQuoteReserves, afterSwapBaseReserves] =
|
|
181
|
+
(0, __1.calculateAmmReservesAfterSwap)(askAmm, 'base', askSize, __1.SwapDirection.REMOVE);
|
|
182
|
+
quoteSwapped = (0, __1.calculateQuoteAssetAmountSwapped)(askAmm.quoteAssetReserve.sub(afterSwapQuoteReserves).abs(), askAmm.pegMultiplier, __1.SwapDirection.REMOVE);
|
|
183
|
+
}
|
|
184
|
+
const price = quoteSwapped.mul(__1.BASE_PRECISION).div(baseSwapped);
|
|
122
185
|
askAmm.baseAssetReserve = afterSwapBaseReserves;
|
|
123
186
|
askAmm.quoteAssetReserve = afterSwapQuoteReserves;
|
|
124
187
|
yield {
|
|
125
188
|
price,
|
|
126
|
-
size:
|
|
127
|
-
sources: { vamm:
|
|
189
|
+
size: baseSwapped,
|
|
190
|
+
sources: { vamm: baseSwapped },
|
|
128
191
|
};
|
|
129
192
|
numAsks++;
|
|
130
193
|
}
|
|
@@ -229,7 +229,7 @@ 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, //
|
|
232
|
+
getQuote({ inputMint, outputMint, amount, maxAccounts, // 50 is an estimated amount with buffer
|
|
233
233
|
slippageBps, swapMode, onlyDirectRoutes, excludeDexes, }: {
|
|
234
234
|
inputMint: PublicKey;
|
|
235
235
|
outputMint: PublicKey;
|
|
@@ -43,7 +43,7 @@ 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 =
|
|
46
|
+
async getQuote({ inputMint, outputMint, amount, maxAccounts = 50, // 50 is an estimated amount with buffer
|
|
47
47
|
slippageBps = 50, swapMode = 'ExactIn', onlyDirectRoutes = false, excludeDexes = [], }) {
|
|
48
48
|
const params = new URLSearchParams({
|
|
49
49
|
inputMint: inputMint.toString(),
|
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.15",
|
|
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,
|
|
@@ -120,11 +121,18 @@ export class DLOBSubscriber {
|
|
|
120
121
|
}
|
|
121
122
|
|
|
122
123
|
if (isPerp && includeVamm) {
|
|
124
|
+
if (fallbackL2Generators.length > 0) {
|
|
125
|
+
throw new Error(
|
|
126
|
+
'includeVamm can only be used if fallbackL2Generators is empty'
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
|
|
123
130
|
fallbackL2Generators = [
|
|
124
131
|
getVammL2Generator({
|
|
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,13 @@ export type L3OrderBook = {
|
|
|
46
48
|
bids: L3Level[];
|
|
47
49
|
};
|
|
48
50
|
|
|
51
|
+
export const DEFAULT_TOP_OF_BOOK_QUOTE_AMOUNTS = [
|
|
52
|
+
new BN(500).mul(QUOTE_PRECISION),
|
|
53
|
+
new BN(1000).mul(QUOTE_PRECISION),
|
|
54
|
+
new BN(2000).mul(QUOTE_PRECISION),
|
|
55
|
+
new BN(5000).mul(QUOTE_PRECISION),
|
|
56
|
+
];
|
|
57
|
+
|
|
49
58
|
/**
|
|
50
59
|
* Get an {@link Generator<L2Level>} generator from a {@link Generator<DLOBNode>}
|
|
51
60
|
* @param dlobNodes e.g. {@link DLOB#getMakerLimitAsks} or {@link DLOB#getMakerLimitBids}
|
|
@@ -139,12 +148,20 @@ export function getVammL2Generator({
|
|
|
139
148
|
oraclePriceData,
|
|
140
149
|
numOrders,
|
|
141
150
|
now,
|
|
151
|
+
topOfBookQuoteAmounts,
|
|
142
152
|
}: {
|
|
143
153
|
marketAccount: PerpMarketAccount;
|
|
144
154
|
oraclePriceData: OraclePriceData;
|
|
145
155
|
numOrders: number;
|
|
146
156
|
now?: BN;
|
|
157
|
+
topOfBookQuoteAmounts?: BN[];
|
|
147
158
|
}): L2OrderBookGenerator {
|
|
159
|
+
let numBaseOrders = numOrders;
|
|
160
|
+
if (topOfBookQuoteAmounts) {
|
|
161
|
+
numBaseOrders = numOrders - topOfBookQuoteAmounts.length;
|
|
162
|
+
assert(topOfBookQuoteAmounts.length < numOrders);
|
|
163
|
+
}
|
|
164
|
+
|
|
148
165
|
const updatedAmm = calculateUpdatedAMM(marketAccount.amm, oraclePriceData);
|
|
149
166
|
|
|
150
167
|
const [openBids, openAsks] = calculateMarketOpenBidAsk(
|
|
@@ -162,38 +179,78 @@ export function getVammL2Generator({
|
|
|
162
179
|
);
|
|
163
180
|
|
|
164
181
|
let numBids = 0;
|
|
165
|
-
|
|
182
|
+
|
|
183
|
+
let topOfBookBidSize = ZERO;
|
|
184
|
+
let bidSize = openBids.div(new BN(numBaseOrders));
|
|
166
185
|
const bidAmm = {
|
|
167
186
|
baseAssetReserve: bidReserves.baseAssetReserve,
|
|
168
187
|
quoteAssetReserve: bidReserves.quoteAssetReserve,
|
|
169
188
|
sqrtK: updatedAmm.sqrtK,
|
|
170
189
|
pegMultiplier: updatedAmm.pegMultiplier,
|
|
171
190
|
};
|
|
191
|
+
|
|
172
192
|
const getL2Bids = function* () {
|
|
173
|
-
while (numBids < numOrders &&
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
193
|
+
while (numBids < numOrders && bidSize.gt(ZERO)) {
|
|
194
|
+
let quoteSwapped = ZERO;
|
|
195
|
+
let baseSwapped = ZERO;
|
|
196
|
+
let [afterSwapQuoteReserves, afterSwapBaseReserves] = [ZERO, ZERO];
|
|
197
|
+
|
|
198
|
+
if (topOfBookQuoteAmounts && numBids < topOfBookQuoteAmounts?.length) {
|
|
199
|
+
const remainingBaseLiquidity = openBids.sub(topOfBookBidSize);
|
|
200
|
+
quoteSwapped = topOfBookQuoteAmounts[numBids];
|
|
201
|
+
[afterSwapQuoteReserves, afterSwapBaseReserves] =
|
|
202
|
+
calculateAmmReservesAfterSwap(
|
|
203
|
+
bidAmm,
|
|
204
|
+
'quote',
|
|
205
|
+
quoteSwapped,
|
|
206
|
+
SwapDirection.REMOVE
|
|
207
|
+
);
|
|
208
|
+
|
|
209
|
+
baseSwapped = bidAmm.baseAssetReserve.sub(afterSwapBaseReserves).abs();
|
|
210
|
+
if (remainingBaseLiquidity.lt(baseSwapped)) {
|
|
211
|
+
baseSwapped = remainingBaseLiquidity;
|
|
212
|
+
[afterSwapQuoteReserves, afterSwapBaseReserves] =
|
|
213
|
+
calculateAmmReservesAfterSwap(
|
|
214
|
+
bidAmm,
|
|
215
|
+
'base',
|
|
216
|
+
baseSwapped,
|
|
217
|
+
SwapDirection.ADD
|
|
218
|
+
);
|
|
219
|
+
|
|
220
|
+
quoteSwapped = calculateQuoteAssetAmountSwapped(
|
|
221
|
+
bidAmm.quoteAssetReserve.sub(afterSwapQuoteReserves).abs(),
|
|
222
|
+
bidAmm.pegMultiplier,
|
|
223
|
+
SwapDirection.ADD
|
|
224
|
+
);
|
|
225
|
+
}
|
|
226
|
+
topOfBookBidSize = topOfBookBidSize.add(baseSwapped);
|
|
227
|
+
bidSize = openBids.sub(topOfBookBidSize).div(new BN(numBaseOrders));
|
|
228
|
+
} else {
|
|
229
|
+
baseSwapped = bidSize;
|
|
230
|
+
[afterSwapQuoteReserves, afterSwapBaseReserves] =
|
|
231
|
+
calculateAmmReservesAfterSwap(
|
|
232
|
+
bidAmm,
|
|
233
|
+
'base',
|
|
234
|
+
baseSwapped,
|
|
235
|
+
SwapDirection.ADD
|
|
236
|
+
);
|
|
237
|
+
|
|
238
|
+
quoteSwapped = calculateQuoteAssetAmountSwapped(
|
|
239
|
+
bidAmm.quoteAssetReserve.sub(afterSwapQuoteReserves).abs(),
|
|
240
|
+
bidAmm.pegMultiplier,
|
|
179
241
|
SwapDirection.ADD
|
|
180
242
|
);
|
|
243
|
+
}
|
|
181
244
|
|
|
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);
|
|
245
|
+
const price = quoteSwapped.mul(BASE_PRECISION).div(baseSwapped);
|
|
189
246
|
|
|
190
247
|
bidAmm.baseAssetReserve = afterSwapBaseReserves;
|
|
191
248
|
bidAmm.quoteAssetReserve = afterSwapQuoteReserves;
|
|
192
249
|
|
|
193
250
|
yield {
|
|
194
251
|
price,
|
|
195
|
-
size:
|
|
196
|
-
sources: { vamm:
|
|
252
|
+
size: baseSwapped,
|
|
253
|
+
sources: { vamm: baseSwapped },
|
|
197
254
|
};
|
|
198
255
|
|
|
199
256
|
numBids++;
|
|
@@ -201,38 +258,82 @@ export function getVammL2Generator({
|
|
|
201
258
|
};
|
|
202
259
|
|
|
203
260
|
let numAsks = 0;
|
|
204
|
-
|
|
261
|
+
let topOfBookAskSize = ZERO;
|
|
262
|
+
let askSize = openAsks.abs().div(new BN(numBaseOrders));
|
|
205
263
|
const askAmm = {
|
|
206
264
|
baseAssetReserve: askReserves.baseAssetReserve,
|
|
207
265
|
quoteAssetReserve: askReserves.quoteAssetReserve,
|
|
208
266
|
sqrtK: updatedAmm.sqrtK,
|
|
209
267
|
pegMultiplier: updatedAmm.pegMultiplier,
|
|
210
268
|
};
|
|
269
|
+
|
|
211
270
|
const getL2Asks = function* () {
|
|
212
271
|
while (numAsks < numOrders && askSize.gt(ZERO)) {
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
272
|
+
let quoteSwapped: BN = ZERO;
|
|
273
|
+
let baseSwapped: BN = ZERO;
|
|
274
|
+
let [afterSwapQuoteReserves, afterSwapBaseReserves] = [ZERO, ZERO];
|
|
275
|
+
|
|
276
|
+
if (topOfBookQuoteAmounts && numAsks < topOfBookQuoteAmounts?.length) {
|
|
277
|
+
const remainingBaseLiquidity = openAsks
|
|
278
|
+
.mul(new BN(-1))
|
|
279
|
+
.sub(topOfBookAskSize);
|
|
280
|
+
quoteSwapped = topOfBookQuoteAmounts[numAsks];
|
|
281
|
+
[afterSwapQuoteReserves, afterSwapBaseReserves] =
|
|
282
|
+
calculateAmmReservesAfterSwap(
|
|
283
|
+
askAmm,
|
|
284
|
+
'quote',
|
|
285
|
+
quoteSwapped,
|
|
286
|
+
SwapDirection.ADD
|
|
287
|
+
);
|
|
288
|
+
|
|
289
|
+
baseSwapped = askAmm.baseAssetReserve.sub(afterSwapBaseReserves).abs();
|
|
290
|
+
if (remainingBaseLiquidity.lt(baseSwapped)) {
|
|
291
|
+
baseSwapped = remainingBaseLiquidity;
|
|
292
|
+
[afterSwapQuoteReserves, afterSwapBaseReserves] =
|
|
293
|
+
calculateAmmReservesAfterSwap(
|
|
294
|
+
bidAmm,
|
|
295
|
+
'base',
|
|
296
|
+
baseSwapped,
|
|
297
|
+
SwapDirection.REMOVE
|
|
298
|
+
);
|
|
299
|
+
|
|
300
|
+
quoteSwapped = calculateQuoteAssetAmountSwapped(
|
|
301
|
+
bidAmm.quoteAssetReserve.sub(afterSwapQuoteReserves).abs(),
|
|
302
|
+
bidAmm.pegMultiplier,
|
|
303
|
+
SwapDirection.REMOVE
|
|
304
|
+
);
|
|
305
|
+
}
|
|
306
|
+
topOfBookAskSize = topOfBookAskSize.add(baseSwapped);
|
|
307
|
+
askSize = openAsks
|
|
308
|
+
.abs()
|
|
309
|
+
.sub(topOfBookAskSize)
|
|
310
|
+
.div(new BN(numBaseOrders));
|
|
311
|
+
} else {
|
|
312
|
+
baseSwapped = askSize;
|
|
313
|
+
[afterSwapQuoteReserves, afterSwapBaseReserves] =
|
|
314
|
+
calculateAmmReservesAfterSwap(
|
|
315
|
+
askAmm,
|
|
316
|
+
'base',
|
|
317
|
+
askSize,
|
|
318
|
+
SwapDirection.REMOVE
|
|
319
|
+
);
|
|
320
|
+
|
|
321
|
+
quoteSwapped = calculateQuoteAssetAmountSwapped(
|
|
322
|
+
askAmm.quoteAssetReserve.sub(afterSwapQuoteReserves).abs(),
|
|
323
|
+
askAmm.pegMultiplier,
|
|
218
324
|
SwapDirection.REMOVE
|
|
219
325
|
);
|
|
326
|
+
}
|
|
220
327
|
|
|
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);
|
|
328
|
+
const price = quoteSwapped.mul(BASE_PRECISION).div(baseSwapped);
|
|
228
329
|
|
|
229
330
|
askAmm.baseAssetReserve = afterSwapBaseReserves;
|
|
230
331
|
askAmm.quoteAssetReserve = afterSwapQuoteReserves;
|
|
231
332
|
|
|
232
333
|
yield {
|
|
233
334
|
price,
|
|
234
|
-
size:
|
|
235
|
-
sources: { vamm:
|
|
335
|
+
size: baseSwapped,
|
|
336
|
+
sources: { vamm: baseSwapped },
|
|
236
337
|
};
|
|
237
338
|
|
|
238
339
|
numAsks++;
|
|
@@ -275,7 +275,7 @@ 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,
|
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
|
});
|