@drift-labs/sdk 2.12.0-beta.0 → 2.12.0-beta.1
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/README.md +63 -48
- package/lib/examples/makeTradeExample.js +18 -19
- package/lib/idl/drift.json +6 -1
- package/lib/math/trade.d.ts +15 -0
- package/lib/math/trade.js +135 -1
- package/package.json +1 -1
- package/src/examples/makeTradeExample.ts +30 -35
- package/src/idl/drift.json +6 -1
- package/src/math/trade.ts +216 -1
package/README.md
CHANGED
|
@@ -11,14 +11,6 @@
|
|
|
11
11
|
</p>
|
|
12
12
|
</div>
|
|
13
13
|
|
|
14
|
-
# Drift Protocol v2
|
|
15
|
-
|
|
16
|
-
This repository provides open source access to Drift's Typescript SDK, Solana Programs, and more.
|
|
17
|
-
|
|
18
|
-
# SDK Guide
|
|
19
|
-
|
|
20
|
-
The technical documentation for the SDK can be found [here](https://drift-labs.github.io/protocol-v2/sdk/), and you can visit Drift's general purpose documentation [here](https://docs.drift.trade/sdk-documentation).
|
|
21
|
-
|
|
22
14
|
## Installation
|
|
23
15
|
|
|
24
16
|
```
|
|
@@ -84,7 +76,7 @@ convertToNumber(new BN(10500), new BN(1000)); // = 10.5
|
|
|
84
76
|
### Setting up an account and making a trade
|
|
85
77
|
|
|
86
78
|
```typescript
|
|
87
|
-
import {
|
|
79
|
+
import { AnchorProvider, BN } from '@project-serum/anchor';
|
|
88
80
|
import { Token, TOKEN_PROGRAM_ID } from '@solana/spl-token';
|
|
89
81
|
import { Connection, Keypair, PublicKey } from '@solana/web3.js';
|
|
90
82
|
import {
|
|
@@ -92,14 +84,18 @@ import {
|
|
|
92
84
|
DriftClient,
|
|
93
85
|
User,
|
|
94
86
|
initialize,
|
|
95
|
-
Markets,
|
|
96
87
|
PositionDirection,
|
|
97
88
|
convertToNumber,
|
|
98
89
|
calculateTradeSlippage,
|
|
99
90
|
PRICE_PRECISION,
|
|
100
91
|
QUOTE_PRECISION,
|
|
101
92
|
Wallet,
|
|
102
|
-
|
|
93
|
+
PerpMarkets,
|
|
94
|
+
BASE_PRECISION,
|
|
95
|
+
getMarketOrderParams,
|
|
96
|
+
BulkAccountLoader,
|
|
97
|
+
getMarketsAndOraclesForSubscription
|
|
98
|
+
} from '../sdk';
|
|
103
99
|
|
|
104
100
|
export const getTokenAddress = (
|
|
105
101
|
mintAddress: string,
|
|
@@ -114,8 +110,9 @@ export const getTokenAddress = (
|
|
|
114
110
|
};
|
|
115
111
|
|
|
116
112
|
const main = async () => {
|
|
113
|
+
const env = 'devnet';
|
|
117
114
|
// Initialize Drift SDK
|
|
118
|
-
const sdkConfig = initialize({ env
|
|
115
|
+
const sdkConfig = initialize({ env });
|
|
119
116
|
|
|
120
117
|
// Set up the Wallet and Provider
|
|
121
118
|
const privateKey = process.env.BOT_PRIVATE_KEY; // stored as an array string
|
|
@@ -129,7 +126,11 @@ const main = async () => {
|
|
|
129
126
|
const connection = new Connection(rpcAddress);
|
|
130
127
|
|
|
131
128
|
// Set up the Provider
|
|
132
|
-
const provider = new
|
|
129
|
+
const provider = new AnchorProvider(
|
|
130
|
+
connection,
|
|
131
|
+
wallet,
|
|
132
|
+
AnchorProvider.defaultOptions()
|
|
133
|
+
);
|
|
133
134
|
|
|
134
135
|
// Check SOL Balance
|
|
135
136
|
const lamportsBalance = await connection.getBalance(wallet.publicKey);
|
|
@@ -142,18 +143,35 @@ const main = async () => {
|
|
|
142
143
|
);
|
|
143
144
|
|
|
144
145
|
// Set up the Drift Client
|
|
145
|
-
const
|
|
146
|
-
const
|
|
146
|
+
const driftPublicKey = new PublicKey(sdkConfig.DRIFT_PROGRAM_ID);
|
|
147
|
+
const bulkAccountLoader = new BulkAccountLoader(
|
|
147
148
|
connection,
|
|
148
|
-
|
|
149
|
-
|
|
149
|
+
'confirmed',
|
|
150
|
+
1000
|
|
150
151
|
);
|
|
152
|
+
const driftClient = new DriftClient({
|
|
153
|
+
connection,
|
|
154
|
+
wallet: provider.wallet,
|
|
155
|
+
programID: driftPublicKey,
|
|
156
|
+
...getMarketsAndOraclesForSubscription(env),
|
|
157
|
+
accountSubscription: {
|
|
158
|
+
type: 'polling',
|
|
159
|
+
accountLoader: bulkAccountLoader,
|
|
160
|
+
},
|
|
161
|
+
});
|
|
151
162
|
await driftClient.subscribe();
|
|
152
163
|
|
|
153
|
-
// Set up
|
|
154
|
-
const user = User
|
|
155
|
-
|
|
156
|
-
|
|
164
|
+
// Set up user client
|
|
165
|
+
const user = new User({
|
|
166
|
+
driftClient: driftClient,
|
|
167
|
+
userAccountPublicKey: await driftClient.getUserAccountPublicKey(),
|
|
168
|
+
accountSubscription: {
|
|
169
|
+
type: 'polling',
|
|
170
|
+
accountLoader: bulkAccountLoader,
|
|
171
|
+
},
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
//// Check if user account exists for the current wallet
|
|
157
175
|
const userAccountExists = await user.exists();
|
|
158
176
|
|
|
159
177
|
if (!userAccountExists) {
|
|
@@ -171,51 +189,48 @@ const main = async () => {
|
|
|
171
189
|
await user.subscribe();
|
|
172
190
|
|
|
173
191
|
// Get current price
|
|
174
|
-
const solMarketInfo =
|
|
192
|
+
const solMarketInfo = PerpMarkets[env].find(
|
|
175
193
|
(market) => market.baseAssetSymbol === 'SOL'
|
|
176
194
|
);
|
|
177
195
|
|
|
178
|
-
const
|
|
179
|
-
driftClient.
|
|
196
|
+
const [bid, ask] = calculateBidAskPrice(
|
|
197
|
+
driftClient.getPerpMarketAccount(marketIndex).amm,
|
|
198
|
+
driftClient.getOracleDataForPerpMarket(marketIndex)
|
|
180
199
|
);
|
|
181
200
|
|
|
182
|
-
const
|
|
201
|
+
const formattedBidPrice = convertToNumber(bid, PRICE_PRECISION);
|
|
202
|
+
const formattedAskPrice = convertToNumber(ask, PRICE_PRECISION);
|
|
183
203
|
|
|
184
|
-
console.log(
|
|
204
|
+
console.log(
|
|
205
|
+
`Current amm bid and ask price are $${formattedBidPrice} and $${formattedAskPrice}`
|
|
206
|
+
);
|
|
185
207
|
|
|
186
208
|
// Estimate the slippage for a $5000 LONG trade
|
|
187
|
-
const solMarketAccount = driftClient.
|
|
209
|
+
const solMarketAccount = driftClient.getPerpMarketAccount(
|
|
210
|
+
solMarketInfo.marketIndex
|
|
211
|
+
);
|
|
188
212
|
|
|
189
213
|
const slippage = convertToNumber(
|
|
190
214
|
calculateTradeSlippage(
|
|
191
215
|
PositionDirection.LONG,
|
|
192
|
-
new BN(
|
|
193
|
-
solMarketAccount
|
|
216
|
+
new BN(1).mul(BASE_PRECISION),
|
|
217
|
+
solMarketAccount,
|
|
218
|
+
'base',
|
|
219
|
+
driftClient.getOracleDataForPerpMarket(solMarketInfo.marketIndex)
|
|
194
220
|
)[0],
|
|
195
221
|
PRICE_PRECISION
|
|
196
222
|
);
|
|
197
223
|
|
|
198
|
-
console.log(
|
|
199
|
-
`Slippage for a $5000 LONG on the SOL market would be $${slippage}`
|
|
200
|
-
);
|
|
224
|
+
console.log(`Slippage for a 1 SOL-PERP would be $${slippage}`);
|
|
201
225
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
226
|
+
await driftClient.placePerpOrder(
|
|
227
|
+
getMarketOrderParams({
|
|
228
|
+
baseAssetAmount: new BN(1).mul(BASE_PRECISION),
|
|
229
|
+
direction: PositionDirection.LONG,
|
|
230
|
+
marketIndex: solMarketAccount.marketIndex,
|
|
231
|
+
})
|
|
207
232
|
);
|
|
208
|
-
console.log(`
|
|
209
|
-
|
|
210
|
-
// Reduce the position by $2000
|
|
211
|
-
await driftClient.openPosition(
|
|
212
|
-
PositionDirection.SHORT,
|
|
213
|
-
new BN(2000).mul(QUOTE_PRECISION),
|
|
214
|
-
solMarketInfo.marketIndex
|
|
215
|
-
);
|
|
216
|
-
|
|
217
|
-
// Close the rest of the position
|
|
218
|
-
await driftClient.closePosition(solMarketInfo.marketIndex);
|
|
233
|
+
console.log(`Placed a 1 SOL-PERP LONG order`);
|
|
219
234
|
};
|
|
220
235
|
|
|
221
236
|
main();
|
|
@@ -11,10 +11,10 @@ const getTokenAddress = (mintAddress, userPubKey) => {
|
|
|
11
11
|
return spl_token_1.Token.getAssociatedTokenAddress(new web3_js_1.PublicKey(`ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL`), spl_token_1.TOKEN_PROGRAM_ID, new web3_js_1.PublicKey(mintAddress), new web3_js_1.PublicKey(userPubKey));
|
|
12
12
|
};
|
|
13
13
|
exports.getTokenAddress = getTokenAddress;
|
|
14
|
-
const
|
|
14
|
+
const env = 'devnet';
|
|
15
15
|
const main = async () => {
|
|
16
16
|
// Initialize Drift SDK
|
|
17
|
-
const sdkConfig = (0, __2.initialize)({ env
|
|
17
|
+
const sdkConfig = (0, __2.initialize)({ env });
|
|
18
18
|
// Set up the Wallet and Provider
|
|
19
19
|
const privateKey = process.env.BOT_PRIVATE_KEY; // stored as an array string
|
|
20
20
|
const keypair = web3_js_1.Keypair.fromSecretKey(Uint8Array.from(JSON.parse(privateKey)));
|
|
@@ -36,14 +36,14 @@ const main = async () => {
|
|
|
36
36
|
connection,
|
|
37
37
|
wallet: provider.wallet,
|
|
38
38
|
programID: driftPublicKey,
|
|
39
|
-
...(0, __2.getMarketsAndOraclesForSubscription)(
|
|
39
|
+
...(0, __2.getMarketsAndOraclesForSubscription)(env),
|
|
40
40
|
accountSubscription: {
|
|
41
41
|
type: 'polling',
|
|
42
42
|
accountLoader: bulkAccountLoader,
|
|
43
43
|
},
|
|
44
44
|
});
|
|
45
45
|
await driftClient.subscribe();
|
|
46
|
-
// Set up
|
|
46
|
+
// Set up user client
|
|
47
47
|
const user = new __2.User({
|
|
48
48
|
driftClient: driftClient,
|
|
49
49
|
userAccountPublicKey: await driftClient.getUserAccountPublicKey(),
|
|
@@ -52,7 +52,7 @@ const main = async () => {
|
|
|
52
52
|
accountLoader: bulkAccountLoader,
|
|
53
53
|
},
|
|
54
54
|
});
|
|
55
|
-
//// Check if
|
|
55
|
+
//// Check if user account exists for the current wallet
|
|
56
56
|
const userAccountExists = await user.exists();
|
|
57
57
|
if (!userAccountExists) {
|
|
58
58
|
//// Create a Clearing House account by Depositing some USDC ($10,000 in this case)
|
|
@@ -62,21 +62,20 @@ const main = async () => {
|
|
|
62
62
|
await user.subscribe();
|
|
63
63
|
// Get current price
|
|
64
64
|
const solMarketInfo = sdkConfig.PERP_MARKETS.find((market) => market.baseAssetSymbol === 'SOL');
|
|
65
|
-
const
|
|
66
|
-
const
|
|
67
|
-
|
|
65
|
+
const marketIndex = solMarketInfo.marketIndex;
|
|
66
|
+
const [bid, ask] = (0, __1.calculateBidAskPrice)(driftClient.getPerpMarketAccount(marketIndex).amm, driftClient.getOracleDataForPerpMarket(marketIndex));
|
|
67
|
+
const formattedBidPrice = (0, __2.convertToNumber)(bid, __2.PRICE_PRECISION);
|
|
68
|
+
const formattedAskPrice = (0, __2.convertToNumber)(ask, __2.PRICE_PRECISION);
|
|
69
|
+
console.log(`Current amm bid and ask price are $${formattedBidPrice} and $${formattedAskPrice}`);
|
|
68
70
|
// Estimate the slippage for a $5000 LONG trade
|
|
69
71
|
const solMarketAccount = driftClient.getPerpMarketAccount(solMarketInfo.marketIndex);
|
|
70
|
-
const
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
await driftClient.openPosition(__2.PositionDirection.SHORT, reduceAmount, solMarketInfo.marketIndex);
|
|
79
|
-
// Close the rest of the position
|
|
80
|
-
await driftClient.closePosition(solMarketInfo.marketIndex);
|
|
72
|
+
const slippage = (0, __2.convertToNumber)((0, __2.calculateTradeSlippage)(__2.PositionDirection.LONG, new anchor_1.BN(1).mul(__1.BASE_PRECISION), solMarketAccount, 'base', driftClient.getOracleDataForPerpMarket(solMarketInfo.marketIndex))[0], __2.PRICE_PRECISION);
|
|
73
|
+
console.log(`Slippage for a 1 SOL-PERP would be $${slippage}`);
|
|
74
|
+
await driftClient.placePerpOrder((0, __1.getMarketOrderParams)({
|
|
75
|
+
baseAssetAmount: new anchor_1.BN(1).mul(__1.BASE_PRECISION),
|
|
76
|
+
direction: __2.PositionDirection.LONG,
|
|
77
|
+
marketIndex: solMarketAccount.marketIndex,
|
|
78
|
+
}));
|
|
79
|
+
console.log(`Placed a 1 SOL-PERP LONG order`);
|
|
81
80
|
};
|
|
82
81
|
main();
|
package/lib/idl/drift.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "2.12.0-beta.
|
|
2
|
+
"version": "2.12.0-beta.1",
|
|
3
3
|
"name": "drift",
|
|
4
4
|
"instructions": [
|
|
5
5
|
{
|
|
@@ -8568,6 +8568,11 @@
|
|
|
8568
8568
|
"code": 6224,
|
|
8569
8569
|
"name": "MaxNumberOfUsers",
|
|
8570
8570
|
"msg": "Max Number Of Users"
|
|
8571
|
+
},
|
|
8572
|
+
{
|
|
8573
|
+
"code": 6225,
|
|
8574
|
+
"name": "InvalidOracleForSettlePnl",
|
|
8575
|
+
"msg": "InvalidOracleForSettlePnl"
|
|
8571
8576
|
}
|
|
8572
8577
|
]
|
|
8573
8578
|
}
|
package/lib/math/trade.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { PerpMarketAccount, PositionDirection } from '../types';
|
|
|
3
3
|
import { BN } from '@project-serum/anchor';
|
|
4
4
|
import { AssetType } from './amm';
|
|
5
5
|
import { OraclePriceData } from '../oracles/types';
|
|
6
|
+
import { DLOB } from '../dlob/DLOB';
|
|
6
7
|
export declare type PriceImpactUnit = 'entryPrice' | 'maxPrice' | 'priceDelta' | 'priceDeltaAsNumber' | 'pctAvg' | 'pctMax' | 'quoteAssetAmount' | 'quoteAssetAmountPeg' | 'acquiredBaseAssetAmount' | 'acquiredQuoteAssetAmount' | 'all';
|
|
7
8
|
/**
|
|
8
9
|
* Calculates avg/max slippage (price impact) for candidate trade
|
|
@@ -52,3 +53,17 @@ export declare function calculateTradeAcquiredAmounts(direction: PositionDirecti
|
|
|
52
53
|
* ]
|
|
53
54
|
*/
|
|
54
55
|
export declare function calculateTargetPriceTrade(market: PerpMarketAccount, targetPrice: BN, pct?: BN, outputAssetType?: AssetType, oraclePriceData?: OraclePriceData, useSpread?: boolean): [PositionDirection, BN, BN, BN];
|
|
56
|
+
/**
|
|
57
|
+
* Calculates the estimated entry price and price impact of order, in base or quote
|
|
58
|
+
* Price impact is based on the difference between the entry price and the best bid/ask price (whether it's dlob or vamm)
|
|
59
|
+
*
|
|
60
|
+
* @param assetType
|
|
61
|
+
* @param amount
|
|
62
|
+
* @param direction
|
|
63
|
+
* @param market
|
|
64
|
+
* @param oraclePriceData
|
|
65
|
+
* @param dlob
|
|
66
|
+
* @param slot
|
|
67
|
+
* @param minPerpAuctionDuration
|
|
68
|
+
*/
|
|
69
|
+
export declare function calculateEstimatedPerpEntryPrice(assetType: AssetType, amount: BN, direction: PositionDirection, market: PerpMarketAccount, oraclePriceData: OraclePriceData, dlob: DLOB, slot: number, minPerpAuctionDuration: number): [BN, BN];
|
package/lib/math/trade.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.calculateTargetPriceTrade = exports.calculateTradeAcquiredAmounts = exports.calculateTradeSlippage = void 0;
|
|
3
|
+
exports.calculateEstimatedPerpEntryPrice = exports.calculateTargetPriceTrade = exports.calculateTradeAcquiredAmounts = exports.calculateTradeSlippage = void 0;
|
|
4
4
|
const types_1 = require("../types");
|
|
5
5
|
const anchor_1 = require("@project-serum/anchor");
|
|
6
6
|
const assert_1 = require("../assert/assert");
|
|
@@ -247,3 +247,137 @@ function calculateTargetPriceTrade(market, targetPrice, pct = MAXPCT, outputAsse
|
|
|
247
247
|
}
|
|
248
248
|
}
|
|
249
249
|
exports.calculateTargetPriceTrade = calculateTargetPriceTrade;
|
|
250
|
+
/**
|
|
251
|
+
* Calculates the estimated entry price and price impact of order, in base or quote
|
|
252
|
+
* Price impact is based on the difference between the entry price and the best bid/ask price (whether it's dlob or vamm)
|
|
253
|
+
*
|
|
254
|
+
* @param assetType
|
|
255
|
+
* @param amount
|
|
256
|
+
* @param direction
|
|
257
|
+
* @param market
|
|
258
|
+
* @param oraclePriceData
|
|
259
|
+
* @param dlob
|
|
260
|
+
* @param slot
|
|
261
|
+
* @param minPerpAuctionDuration
|
|
262
|
+
*/
|
|
263
|
+
function calculateEstimatedPerpEntryPrice(assetType, amount, direction, market, oraclePriceData, dlob, slot, minPerpAuctionDuration) {
|
|
264
|
+
if (amount.eq(numericConstants_1.ZERO)) {
|
|
265
|
+
return [numericConstants_1.ZERO, numericConstants_1.ZERO];
|
|
266
|
+
}
|
|
267
|
+
const takerIsLong = (0, types_2.isVariant)(direction, 'long');
|
|
268
|
+
const limitOrders = dlob[takerIsLong ? 'getRestingLimitAsks' : 'getRestingLimitBids'](market.marketIndex, slot, types_1.MarketType.PERP, oraclePriceData, minPerpAuctionDuration);
|
|
269
|
+
const swapDirection = (0, amm_1.getSwapDirection)(assetType, direction);
|
|
270
|
+
const { baseAssetReserve, quoteAssetReserve, sqrtK, newPeg } = (0, amm_1.calculateUpdatedAMMSpreadReserves)(market.amm, direction, oraclePriceData);
|
|
271
|
+
const amm = {
|
|
272
|
+
baseAssetReserve,
|
|
273
|
+
quoteAssetReserve,
|
|
274
|
+
sqrtK: sqrtK,
|
|
275
|
+
pegMultiplier: newPeg,
|
|
276
|
+
};
|
|
277
|
+
const invariant = amm.sqrtK.mul(amm.sqrtK);
|
|
278
|
+
let initialPrice = (0, amm_1.calculatePrice)(amm.baseAssetReserve, amm.quoteAssetReserve, amm.pegMultiplier);
|
|
279
|
+
let cumulativeBaseFilled = numericConstants_1.ZERO;
|
|
280
|
+
let cumulativeQuoteFilled = numericConstants_1.ZERO;
|
|
281
|
+
let limitOrder = limitOrders.next().value;
|
|
282
|
+
if (limitOrder) {
|
|
283
|
+
const limitOrderPrice = limitOrder.getPrice(oraclePriceData, slot);
|
|
284
|
+
initialPrice = takerIsLong
|
|
285
|
+
? anchor_1.BN.min(limitOrderPrice, initialPrice)
|
|
286
|
+
: anchor_1.BN.max(limitOrderPrice, initialPrice);
|
|
287
|
+
}
|
|
288
|
+
if (assetType === 'base') {
|
|
289
|
+
while (!cumulativeBaseFilled.eq(amount)) {
|
|
290
|
+
const limitOrderPrice = limitOrder === null || limitOrder === void 0 ? void 0 : limitOrder.getPrice(oraclePriceData, slot);
|
|
291
|
+
let maxAmmFill;
|
|
292
|
+
if (limitOrderPrice) {
|
|
293
|
+
const newBaseReserves = (0, utils_1.squareRootBN)(invariant
|
|
294
|
+
.mul(numericConstants_1.PRICE_PRECISION)
|
|
295
|
+
.mul(amm.pegMultiplier)
|
|
296
|
+
.div(limitOrderPrice)
|
|
297
|
+
.div(numericConstants_1.PEG_PRECISION));
|
|
298
|
+
// will be zero if the limit order price is better than the amm price
|
|
299
|
+
maxAmmFill = takerIsLong
|
|
300
|
+
? amm.baseAssetReserve.sub(newBaseReserves)
|
|
301
|
+
: newBaseReserves.sub(amm.baseAssetReserve);
|
|
302
|
+
}
|
|
303
|
+
else {
|
|
304
|
+
maxAmmFill = amount.sub(cumulativeBaseFilled);
|
|
305
|
+
}
|
|
306
|
+
if (maxAmmFill.gt(numericConstants_1.ZERO)) {
|
|
307
|
+
const baseFilled = anchor_1.BN.min(amount.sub(cumulativeBaseFilled), maxAmmFill);
|
|
308
|
+
const [afterSwapQuoteReserves, afterSwapBaseReserves] = (0, amm_1.calculateAmmReservesAfterSwap)(amm, 'base', baseFilled, swapDirection);
|
|
309
|
+
const quoteFilled = (0, amm_1.calculateQuoteAssetAmountSwapped)(amm.quoteAssetReserve.sub(afterSwapQuoteReserves).abs(), amm.pegMultiplier, swapDirection);
|
|
310
|
+
cumulativeBaseFilled = cumulativeBaseFilled.add(baseFilled);
|
|
311
|
+
cumulativeQuoteFilled = cumulativeQuoteFilled.add(quoteFilled);
|
|
312
|
+
amm.baseAssetReserve = afterSwapBaseReserves;
|
|
313
|
+
amm.quoteAssetReserve = afterSwapQuoteReserves;
|
|
314
|
+
if (cumulativeBaseFilled.eq(amount)) {
|
|
315
|
+
break;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
const baseFilled = anchor_1.BN.min(limitOrder.order.baseAssetAmount.sub(limitOrder.order.baseAssetAmountFilled), amount.sub(cumulativeBaseFilled));
|
|
319
|
+
const quoteFilled = baseFilled.mul(limitOrderPrice).div(numericConstants_1.BASE_PRECISION);
|
|
320
|
+
cumulativeBaseFilled = cumulativeBaseFilled.add(baseFilled);
|
|
321
|
+
cumulativeQuoteFilled = cumulativeQuoteFilled.add(quoteFilled);
|
|
322
|
+
if (cumulativeBaseFilled.eq(amount)) {
|
|
323
|
+
break;
|
|
324
|
+
}
|
|
325
|
+
limitOrder = limitOrders.next().value;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
else {
|
|
329
|
+
while (!cumulativeQuoteFilled.eq(amount)) {
|
|
330
|
+
const limitOrderPrice = limitOrder === null || limitOrder === void 0 ? void 0 : limitOrder.getPrice(oraclePriceData, slot);
|
|
331
|
+
let maxAmmFill;
|
|
332
|
+
if (limitOrderPrice) {
|
|
333
|
+
const newQuoteReserves = (0, utils_1.squareRootBN)(invariant
|
|
334
|
+
.mul(numericConstants_1.PEG_PRECISION)
|
|
335
|
+
.mul(limitOrderPrice)
|
|
336
|
+
.div(amm.pegMultiplier)
|
|
337
|
+
.div(numericConstants_1.PRICE_PRECISION));
|
|
338
|
+
// will be zero if the limit order price is better than the amm price
|
|
339
|
+
maxAmmFill = takerIsLong
|
|
340
|
+
? newQuoteReserves.sub(amm.quoteAssetReserve)
|
|
341
|
+
: amm.quoteAssetReserve.sub(newQuoteReserves);
|
|
342
|
+
}
|
|
343
|
+
else {
|
|
344
|
+
maxAmmFill = amount.sub(cumulativeQuoteFilled);
|
|
345
|
+
}
|
|
346
|
+
if (maxAmmFill.gt(numericConstants_1.ZERO)) {
|
|
347
|
+
const quoteFilled = anchor_1.BN.min(amount.sub(cumulativeQuoteFilled), maxAmmFill);
|
|
348
|
+
const [afterSwapQuoteReserves, afterSwapBaseReserves] = (0, amm_1.calculateAmmReservesAfterSwap)(amm, 'quote', quoteFilled, swapDirection);
|
|
349
|
+
const baseFilled = afterSwapBaseReserves
|
|
350
|
+
.sub(amm.baseAssetReserve)
|
|
351
|
+
.abs();
|
|
352
|
+
cumulativeBaseFilled = cumulativeBaseFilled.add(baseFilled);
|
|
353
|
+
cumulativeQuoteFilled = cumulativeQuoteFilled.add(quoteFilled);
|
|
354
|
+
amm.baseAssetReserve = afterSwapBaseReserves;
|
|
355
|
+
amm.quoteAssetReserve = afterSwapQuoteReserves;
|
|
356
|
+
if (cumulativeQuoteFilled.eq(amount)) {
|
|
357
|
+
break;
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
const quoteFilled = anchor_1.BN.min(limitOrder.order.baseAssetAmount
|
|
361
|
+
.sub(limitOrder.order.baseAssetAmountFilled)
|
|
362
|
+
.mul(limitOrderPrice)
|
|
363
|
+
.div(numericConstants_1.BASE_PRECISION), amount.sub(cumulativeQuoteFilled));
|
|
364
|
+
const baseFilled = quoteFilled.mul(numericConstants_1.BASE_PRECISION).div(limitOrderPrice);
|
|
365
|
+
cumulativeBaseFilled = cumulativeBaseFilled.add(baseFilled);
|
|
366
|
+
cumulativeQuoteFilled = cumulativeQuoteFilled.add(quoteFilled);
|
|
367
|
+
if (cumulativeQuoteFilled.eq(amount)) {
|
|
368
|
+
break;
|
|
369
|
+
}
|
|
370
|
+
limitOrder = limitOrders.next().value;
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
const entryPrice = cumulativeQuoteFilled
|
|
374
|
+
.mul(numericConstants_1.BASE_PRECISION)
|
|
375
|
+
.div(cumulativeBaseFilled);
|
|
376
|
+
const priceImpact = entryPrice
|
|
377
|
+
.sub(initialPrice)
|
|
378
|
+
.mul(numericConstants_1.PRICE_PRECISION)
|
|
379
|
+
.div(initialPrice)
|
|
380
|
+
.abs();
|
|
381
|
+
return [entryPrice, priceImpact];
|
|
382
|
+
}
|
|
383
|
+
exports.calculateEstimatedPerpEntryPrice = calculateEstimatedPerpEntryPrice;
|
package/package.json
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import { AnchorProvider, BN } from '@project-serum/anchor';
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
BASE_PRECISION,
|
|
4
|
+
calculateBidAskPrice,
|
|
5
|
+
getMarketOrderParams,
|
|
6
|
+
Wallet,
|
|
7
|
+
} from '..';
|
|
3
8
|
import { Token, TOKEN_PROGRAM_ID } from '@solana/spl-token';
|
|
4
9
|
import { Connection, Keypair, PublicKey } from '@solana/web3.js';
|
|
5
10
|
import {
|
|
6
|
-
calculateReservePrice,
|
|
7
11
|
DriftClient,
|
|
8
12
|
User,
|
|
9
13
|
initialize,
|
|
@@ -29,11 +33,11 @@ export const getTokenAddress = (
|
|
|
29
33
|
);
|
|
30
34
|
};
|
|
31
35
|
|
|
32
|
-
const
|
|
36
|
+
const env = 'devnet';
|
|
33
37
|
|
|
34
38
|
const main = async () => {
|
|
35
39
|
// Initialize Drift SDK
|
|
36
|
-
const sdkConfig = initialize({ env
|
|
40
|
+
const sdkConfig = initialize({ env });
|
|
37
41
|
|
|
38
42
|
// Set up the Wallet and Provider
|
|
39
43
|
const privateKey = process.env.BOT_PRIVATE_KEY; // stored as an array string
|
|
@@ -74,7 +78,7 @@ const main = async () => {
|
|
|
74
78
|
connection,
|
|
75
79
|
wallet: provider.wallet,
|
|
76
80
|
programID: driftPublicKey,
|
|
77
|
-
...getMarketsAndOraclesForSubscription(
|
|
81
|
+
...getMarketsAndOraclesForSubscription(env),
|
|
78
82
|
accountSubscription: {
|
|
79
83
|
type: 'polling',
|
|
80
84
|
accountLoader: bulkAccountLoader,
|
|
@@ -82,7 +86,7 @@ const main = async () => {
|
|
|
82
86
|
});
|
|
83
87
|
await driftClient.subscribe();
|
|
84
88
|
|
|
85
|
-
// Set up
|
|
89
|
+
// Set up user client
|
|
86
90
|
const user = new User({
|
|
87
91
|
driftClient: driftClient,
|
|
88
92
|
userAccountPublicKey: await driftClient.getUserAccountPublicKey(),
|
|
@@ -92,7 +96,7 @@ const main = async () => {
|
|
|
92
96
|
},
|
|
93
97
|
});
|
|
94
98
|
|
|
95
|
-
//// Check if
|
|
99
|
+
//// Check if user account exists for the current wallet
|
|
96
100
|
const userAccountExists = await user.exists();
|
|
97
101
|
|
|
98
102
|
if (!userAccountExists) {
|
|
@@ -115,54 +119,45 @@ const main = async () => {
|
|
|
115
119
|
(market) => market.baseAssetSymbol === 'SOL'
|
|
116
120
|
);
|
|
117
121
|
|
|
118
|
-
const
|
|
119
|
-
|
|
120
|
-
|
|
122
|
+
const marketIndex = solMarketInfo.marketIndex;
|
|
123
|
+
const [bid, ask] = calculateBidAskPrice(
|
|
124
|
+
driftClient.getPerpMarketAccount(marketIndex).amm,
|
|
125
|
+
driftClient.getOracleDataForPerpMarket(marketIndex)
|
|
121
126
|
);
|
|
122
127
|
|
|
123
|
-
const
|
|
128
|
+
const formattedBidPrice = convertToNumber(bid, PRICE_PRECISION);
|
|
129
|
+
const formattedAskPrice = convertToNumber(ask, PRICE_PRECISION);
|
|
124
130
|
|
|
125
|
-
console.log(
|
|
131
|
+
console.log(
|
|
132
|
+
`Current amm bid and ask price are $${formattedBidPrice} and $${formattedAskPrice}`
|
|
133
|
+
);
|
|
126
134
|
|
|
127
135
|
// Estimate the slippage for a $5000 LONG trade
|
|
128
136
|
const solMarketAccount = driftClient.getPerpMarketAccount(
|
|
129
137
|
solMarketInfo.marketIndex
|
|
130
138
|
);
|
|
131
139
|
|
|
132
|
-
const longAmount = new BN(5000).mul(QUOTE_PRECISION);
|
|
133
140
|
const slippage = convertToNumber(
|
|
134
141
|
calculateTradeSlippage(
|
|
135
142
|
PositionDirection.LONG,
|
|
136
|
-
|
|
143
|
+
new BN(1).mul(BASE_PRECISION),
|
|
137
144
|
solMarketAccount,
|
|
138
|
-
'
|
|
145
|
+
'base',
|
|
139
146
|
driftClient.getOracleDataForPerpMarket(solMarketInfo.marketIndex)
|
|
140
147
|
)[0],
|
|
141
148
|
PRICE_PRECISION
|
|
142
149
|
);
|
|
143
150
|
|
|
144
|
-
console.log(
|
|
145
|
-
`Slippage for a $5000 LONG on the SOL market would be $${slippage}`
|
|
146
|
-
);
|
|
151
|
+
console.log(`Slippage for a 1 SOL-PERP would be $${slippage}`);
|
|
147
152
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
+
await driftClient.placePerpOrder(
|
|
154
|
+
getMarketOrderParams({
|
|
155
|
+
baseAssetAmount: new BN(1).mul(BASE_PRECISION),
|
|
156
|
+
direction: PositionDirection.LONG,
|
|
157
|
+
marketIndex: solMarketAccount.marketIndex,
|
|
158
|
+
})
|
|
153
159
|
);
|
|
154
|
-
console.log(`
|
|
155
|
-
|
|
156
|
-
// Reduce the position by $2000
|
|
157
|
-
const reduceAmount = new BN(2000).mul(QUOTE_PRECISION);
|
|
158
|
-
await driftClient.openPosition(
|
|
159
|
-
PositionDirection.SHORT,
|
|
160
|
-
reduceAmount,
|
|
161
|
-
solMarketInfo.marketIndex
|
|
162
|
-
);
|
|
163
|
-
|
|
164
|
-
// Close the rest of the position
|
|
165
|
-
await driftClient.closePosition(solMarketInfo.marketIndex);
|
|
160
|
+
console.log(`Placed a 1 SOL-PERP LONG order`);
|
|
166
161
|
};
|
|
167
162
|
|
|
168
163
|
main();
|
package/src/idl/drift.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "2.12.0-beta.
|
|
2
|
+
"version": "2.12.0-beta.1",
|
|
3
3
|
"name": "drift",
|
|
4
4
|
"instructions": [
|
|
5
5
|
{
|
|
@@ -8568,6 +8568,11 @@
|
|
|
8568
8568
|
"code": 6224,
|
|
8569
8569
|
"name": "MaxNumberOfUsers",
|
|
8570
8570
|
"msg": "Max Number Of Users"
|
|
8571
|
+
},
|
|
8572
|
+
{
|
|
8573
|
+
"code": 6225,
|
|
8574
|
+
"name": "InvalidOracleForSettlePnl",
|
|
8575
|
+
"msg": "InvalidOracleForSettlePnl"
|
|
8571
8576
|
}
|
|
8572
8577
|
]
|
|
8573
8578
|
}
|
package/src/math/trade.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { PerpMarketAccount, PositionDirection } from '../types';
|
|
1
|
+
import { MarketType, PerpMarketAccount, PositionDirection } from '../types';
|
|
2
2
|
import { BN } from '@project-serum/anchor';
|
|
3
3
|
import { assert } from '../assert/assert';
|
|
4
4
|
import {
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
PEG_PRECISION,
|
|
7
7
|
AMM_TO_QUOTE_PRECISION_RATIO,
|
|
8
8
|
ZERO,
|
|
9
|
+
BASE_PRECISION,
|
|
9
10
|
} from '../constants/numericConstants';
|
|
10
11
|
import {
|
|
11
12
|
calculateBidPrice,
|
|
@@ -23,6 +24,7 @@ import {
|
|
|
23
24
|
import { squareRootBN } from './utils';
|
|
24
25
|
import { isVariant } from '../types';
|
|
25
26
|
import { OraclePriceData } from '../oracles/types';
|
|
27
|
+
import { DLOB } from '../dlob/DLOB';
|
|
26
28
|
|
|
27
29
|
const MAXPCT = new BN(1000); //percentage units are [0,1000] => [0,1]
|
|
28
30
|
|
|
@@ -349,3 +351,216 @@ export function calculateTargetPriceTrade(
|
|
|
349
351
|
return [direction, baseSize, entryPrice, targetPrice];
|
|
350
352
|
}
|
|
351
353
|
}
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* Calculates the estimated entry price and price impact of order, in base or quote
|
|
357
|
+
* Price impact is based on the difference between the entry price and the best bid/ask price (whether it's dlob or vamm)
|
|
358
|
+
*
|
|
359
|
+
* @param assetType
|
|
360
|
+
* @param amount
|
|
361
|
+
* @param direction
|
|
362
|
+
* @param market
|
|
363
|
+
* @param oraclePriceData
|
|
364
|
+
* @param dlob
|
|
365
|
+
* @param slot
|
|
366
|
+
* @param minPerpAuctionDuration
|
|
367
|
+
*/
|
|
368
|
+
export function calculateEstimatedPerpEntryPrice(
|
|
369
|
+
assetType: AssetType,
|
|
370
|
+
amount: BN,
|
|
371
|
+
direction: PositionDirection,
|
|
372
|
+
market: PerpMarketAccount,
|
|
373
|
+
oraclePriceData: OraclePriceData,
|
|
374
|
+
dlob: DLOB,
|
|
375
|
+
slot: number,
|
|
376
|
+
minPerpAuctionDuration: number
|
|
377
|
+
): [BN, BN] {
|
|
378
|
+
if (amount.eq(ZERO)) {
|
|
379
|
+
return [ZERO, ZERO];
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
const takerIsLong = isVariant(direction, 'long');
|
|
383
|
+
const limitOrders = dlob[
|
|
384
|
+
takerIsLong ? 'getRestingLimitAsks' : 'getRestingLimitBids'
|
|
385
|
+
](
|
|
386
|
+
market.marketIndex,
|
|
387
|
+
slot,
|
|
388
|
+
MarketType.PERP,
|
|
389
|
+
oraclePriceData,
|
|
390
|
+
minPerpAuctionDuration
|
|
391
|
+
);
|
|
392
|
+
|
|
393
|
+
const swapDirection = getSwapDirection(assetType, direction);
|
|
394
|
+
|
|
395
|
+
const { baseAssetReserve, quoteAssetReserve, sqrtK, newPeg } =
|
|
396
|
+
calculateUpdatedAMMSpreadReserves(market.amm, direction, oraclePriceData);
|
|
397
|
+
const amm = {
|
|
398
|
+
baseAssetReserve,
|
|
399
|
+
quoteAssetReserve,
|
|
400
|
+
sqrtK: sqrtK,
|
|
401
|
+
pegMultiplier: newPeg,
|
|
402
|
+
};
|
|
403
|
+
|
|
404
|
+
const invariant = amm.sqrtK.mul(amm.sqrtK);
|
|
405
|
+
|
|
406
|
+
let initialPrice = calculatePrice(
|
|
407
|
+
amm.baseAssetReserve,
|
|
408
|
+
amm.quoteAssetReserve,
|
|
409
|
+
amm.pegMultiplier
|
|
410
|
+
);
|
|
411
|
+
|
|
412
|
+
let cumulativeBaseFilled = ZERO;
|
|
413
|
+
let cumulativeQuoteFilled = ZERO;
|
|
414
|
+
|
|
415
|
+
let limitOrder = limitOrders.next().value;
|
|
416
|
+
if (limitOrder) {
|
|
417
|
+
const limitOrderPrice = limitOrder.getPrice(oraclePriceData, slot);
|
|
418
|
+
initialPrice = takerIsLong
|
|
419
|
+
? BN.min(limitOrderPrice, initialPrice)
|
|
420
|
+
: BN.max(limitOrderPrice, initialPrice);
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
if (assetType === 'base') {
|
|
424
|
+
while (!cumulativeBaseFilled.eq(amount)) {
|
|
425
|
+
const limitOrderPrice = limitOrder?.getPrice(oraclePriceData, slot);
|
|
426
|
+
|
|
427
|
+
let maxAmmFill: BN;
|
|
428
|
+
if (limitOrderPrice) {
|
|
429
|
+
const newBaseReserves = squareRootBN(
|
|
430
|
+
invariant
|
|
431
|
+
.mul(PRICE_PRECISION)
|
|
432
|
+
.mul(amm.pegMultiplier)
|
|
433
|
+
.div(limitOrderPrice)
|
|
434
|
+
.div(PEG_PRECISION)
|
|
435
|
+
);
|
|
436
|
+
|
|
437
|
+
// will be zero if the limit order price is better than the amm price
|
|
438
|
+
maxAmmFill = takerIsLong
|
|
439
|
+
? amm.baseAssetReserve.sub(newBaseReserves)
|
|
440
|
+
: newBaseReserves.sub(amm.baseAssetReserve);
|
|
441
|
+
} else {
|
|
442
|
+
maxAmmFill = amount.sub(cumulativeBaseFilled);
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
if (maxAmmFill.gt(ZERO)) {
|
|
446
|
+
const baseFilled = BN.min(amount.sub(cumulativeBaseFilled), maxAmmFill);
|
|
447
|
+
const [afterSwapQuoteReserves, afterSwapBaseReserves] =
|
|
448
|
+
calculateAmmReservesAfterSwap(amm, 'base', baseFilled, swapDirection);
|
|
449
|
+
|
|
450
|
+
const quoteFilled = calculateQuoteAssetAmountSwapped(
|
|
451
|
+
amm.quoteAssetReserve.sub(afterSwapQuoteReserves).abs(),
|
|
452
|
+
amm.pegMultiplier,
|
|
453
|
+
swapDirection
|
|
454
|
+
);
|
|
455
|
+
|
|
456
|
+
cumulativeBaseFilled = cumulativeBaseFilled.add(baseFilled);
|
|
457
|
+
cumulativeQuoteFilled = cumulativeQuoteFilled.add(quoteFilled);
|
|
458
|
+
|
|
459
|
+
amm.baseAssetReserve = afterSwapBaseReserves;
|
|
460
|
+
amm.quoteAssetReserve = afterSwapQuoteReserves;
|
|
461
|
+
|
|
462
|
+
if (cumulativeBaseFilled.eq(amount)) {
|
|
463
|
+
break;
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
const baseFilled = BN.min(
|
|
468
|
+
limitOrder.order.baseAssetAmount.sub(
|
|
469
|
+
limitOrder.order.baseAssetAmountFilled
|
|
470
|
+
),
|
|
471
|
+
amount.sub(cumulativeBaseFilled)
|
|
472
|
+
);
|
|
473
|
+
const quoteFilled = baseFilled.mul(limitOrderPrice).div(BASE_PRECISION);
|
|
474
|
+
|
|
475
|
+
cumulativeBaseFilled = cumulativeBaseFilled.add(baseFilled);
|
|
476
|
+
cumulativeQuoteFilled = cumulativeQuoteFilled.add(quoteFilled);
|
|
477
|
+
|
|
478
|
+
if (cumulativeBaseFilled.eq(amount)) {
|
|
479
|
+
break;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
limitOrder = limitOrders.next().value;
|
|
483
|
+
}
|
|
484
|
+
} else {
|
|
485
|
+
while (!cumulativeQuoteFilled.eq(amount)) {
|
|
486
|
+
const limitOrderPrice = limitOrder?.getPrice(oraclePriceData, slot);
|
|
487
|
+
|
|
488
|
+
let maxAmmFill: BN;
|
|
489
|
+
if (limitOrderPrice) {
|
|
490
|
+
const newQuoteReserves = squareRootBN(
|
|
491
|
+
invariant
|
|
492
|
+
.mul(PEG_PRECISION)
|
|
493
|
+
.mul(limitOrderPrice)
|
|
494
|
+
.div(amm.pegMultiplier)
|
|
495
|
+
.div(PRICE_PRECISION)
|
|
496
|
+
);
|
|
497
|
+
|
|
498
|
+
// will be zero if the limit order price is better than the amm price
|
|
499
|
+
maxAmmFill = takerIsLong
|
|
500
|
+
? newQuoteReserves.sub(amm.quoteAssetReserve)
|
|
501
|
+
: amm.quoteAssetReserve.sub(newQuoteReserves);
|
|
502
|
+
} else {
|
|
503
|
+
maxAmmFill = amount.sub(cumulativeQuoteFilled);
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
if (maxAmmFill.gt(ZERO)) {
|
|
507
|
+
const quoteFilled = BN.min(
|
|
508
|
+
amount.sub(cumulativeQuoteFilled),
|
|
509
|
+
maxAmmFill
|
|
510
|
+
);
|
|
511
|
+
const [afterSwapQuoteReserves, afterSwapBaseReserves] =
|
|
512
|
+
calculateAmmReservesAfterSwap(
|
|
513
|
+
amm,
|
|
514
|
+
'quote',
|
|
515
|
+
quoteFilled,
|
|
516
|
+
swapDirection
|
|
517
|
+
);
|
|
518
|
+
|
|
519
|
+
const baseFilled = afterSwapBaseReserves
|
|
520
|
+
.sub(amm.baseAssetReserve)
|
|
521
|
+
.abs();
|
|
522
|
+
|
|
523
|
+
cumulativeBaseFilled = cumulativeBaseFilled.add(baseFilled);
|
|
524
|
+
cumulativeQuoteFilled = cumulativeQuoteFilled.add(quoteFilled);
|
|
525
|
+
|
|
526
|
+
amm.baseAssetReserve = afterSwapBaseReserves;
|
|
527
|
+
amm.quoteAssetReserve = afterSwapQuoteReserves;
|
|
528
|
+
|
|
529
|
+
if (cumulativeQuoteFilled.eq(amount)) {
|
|
530
|
+
break;
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
const quoteFilled = BN.min(
|
|
535
|
+
limitOrder.order.baseAssetAmount
|
|
536
|
+
.sub(limitOrder.order.baseAssetAmountFilled)
|
|
537
|
+
.mul(limitOrderPrice)
|
|
538
|
+
.div(BASE_PRECISION),
|
|
539
|
+
amount.sub(cumulativeQuoteFilled)
|
|
540
|
+
);
|
|
541
|
+
|
|
542
|
+
const baseFilled = quoteFilled.mul(BASE_PRECISION).div(limitOrderPrice);
|
|
543
|
+
|
|
544
|
+
cumulativeBaseFilled = cumulativeBaseFilled.add(baseFilled);
|
|
545
|
+
cumulativeQuoteFilled = cumulativeQuoteFilled.add(quoteFilled);
|
|
546
|
+
|
|
547
|
+
if (cumulativeQuoteFilled.eq(amount)) {
|
|
548
|
+
break;
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
limitOrder = limitOrders.next().value;
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
const entryPrice = cumulativeQuoteFilled
|
|
556
|
+
.mul(BASE_PRECISION)
|
|
557
|
+
.div(cumulativeBaseFilled);
|
|
558
|
+
|
|
559
|
+
const priceImpact = entryPrice
|
|
560
|
+
.sub(initialPrice)
|
|
561
|
+
.mul(PRICE_PRECISION)
|
|
562
|
+
.div(initialPrice)
|
|
563
|
+
.abs();
|
|
564
|
+
|
|
565
|
+
return [entryPrice, priceImpact];
|
|
566
|
+
}
|