@drift-labs/sdk 0.2.0-master.0 → 0.2.0-master.3
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/lib/admin.d.ts +1 -0
- package/lib/admin.js +9 -0
- package/lib/clearingHouse.d.ts +5 -9
- package/lib/clearingHouse.js +53 -70
- package/lib/config.js +1 -1
- package/lib/constants/numericConstants.d.ts +1 -0
- package/lib/constants/numericConstants.js +2 -1
- package/lib/factory/bigNum.d.ts +8 -2
- package/lib/factory/bigNum.js +14 -6
- package/lib/idl/clearing_house.json +106 -39
- package/lib/math/amm.d.ts +6 -1
- package/lib/math/amm.js +127 -16
- package/lib/math/orders.js +2 -12
- package/lib/math/repeg.js +1 -1
- package/lib/orders.d.ts +1 -2
- package/lib/orders.js +6 -85
- package/lib/types.d.ts +5 -0
- package/lib/types.js +1 -0
- package/package.json +2 -2
- package/src/admin.ts +13 -0
- package/src/clearingHouse.ts +75 -126
- package/src/config.ts +1 -1
- package/src/constants/numericConstants.ts +1 -0
- package/src/factory/bigNum.ts +26 -9
- package/src/idl/clearing_house.json +106 -39
- package/src/math/amm.ts +202 -17
- package/src/math/orders.ts +3 -13
- package/src/math/repeg.ts +2 -1
- package/src/orders.ts +7 -131
- package/src/types.ts +4 -1
package/src/math/amm.ts
CHANGED
|
@@ -6,8 +6,10 @@ import {
|
|
|
6
6
|
ZERO,
|
|
7
7
|
BID_ASK_SPREAD_PRECISION,
|
|
8
8
|
ONE,
|
|
9
|
-
// QUOTE_PRECISION,
|
|
10
9
|
AMM_TO_QUOTE_PRECISION_RATIO,
|
|
10
|
+
QUOTE_PRECISION,
|
|
11
|
+
MARGIN_PRECISION,
|
|
12
|
+
PRICE_DIV_PEG,
|
|
11
13
|
} from '../constants/numericConstants';
|
|
12
14
|
import {
|
|
13
15
|
AMM,
|
|
@@ -25,6 +27,70 @@ import {
|
|
|
25
27
|
calculateAdjustKCost,
|
|
26
28
|
calculateBudgetedPeg,
|
|
27
29
|
} from './repeg';
|
|
30
|
+
|
|
31
|
+
export function calculatePegFromTargetPrice(
|
|
32
|
+
targetPrice: BN,
|
|
33
|
+
baseAssetReserve: BN,
|
|
34
|
+
quoteAssetReserve: BN
|
|
35
|
+
): BN {
|
|
36
|
+
return targetPrice
|
|
37
|
+
.mul(baseAssetReserve)
|
|
38
|
+
.div(quoteAssetReserve)
|
|
39
|
+
.add(PRICE_DIV_PEG.div(new BN(2)))
|
|
40
|
+
.div(PRICE_DIV_PEG);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function calculateOptimalPegAndBudget(
|
|
44
|
+
amm: AMM,
|
|
45
|
+
oraclePriceData: OraclePriceData
|
|
46
|
+
): [BN, BN, BN, boolean] {
|
|
47
|
+
const markPriceBefore = calculatePrice(
|
|
48
|
+
amm.baseAssetReserve,
|
|
49
|
+
amm.quoteAssetReserve,
|
|
50
|
+
amm.pegMultiplier
|
|
51
|
+
);
|
|
52
|
+
const targetPrice = oraclePriceData.price;
|
|
53
|
+
const newPeg = calculatePegFromTargetPrice(
|
|
54
|
+
targetPrice,
|
|
55
|
+
amm.baseAssetReserve,
|
|
56
|
+
amm.quoteAssetReserve
|
|
57
|
+
);
|
|
58
|
+
const prePegCost = calculateRepegCost(amm, newPeg);
|
|
59
|
+
|
|
60
|
+
const totalFeeLB = amm.totalExchangeFee.div(new BN(2));
|
|
61
|
+
const budget = BN.max(ZERO, amm.totalFeeMinusDistributions.sub(totalFeeLB));
|
|
62
|
+
if (budget.lt(prePegCost)) {
|
|
63
|
+
const maxPriceSpread = new BN(amm.maxSpread)
|
|
64
|
+
.mul(targetPrice)
|
|
65
|
+
.div(BID_ASK_SPREAD_PRECISION);
|
|
66
|
+
|
|
67
|
+
let newTargetPrice: BN;
|
|
68
|
+
let newOptimalPeg: BN;
|
|
69
|
+
let newBudget: BN;
|
|
70
|
+
const targetPriceGap = markPriceBefore.sub(targetPrice);
|
|
71
|
+
if (targetPriceGap.abs().gt(maxPriceSpread)) {
|
|
72
|
+
const markAdj = targetPriceGap.abs().sub(maxPriceSpread);
|
|
73
|
+
|
|
74
|
+
if (targetPriceGap.lt(new BN(0))) {
|
|
75
|
+
newTargetPrice = markPriceBefore.add(markAdj);
|
|
76
|
+
} else {
|
|
77
|
+
newTargetPrice = markPriceBefore.sub(markAdj);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
newOptimalPeg = calculatePegFromTargetPrice(
|
|
81
|
+
newTargetPrice,
|
|
82
|
+
amm.baseAssetReserve,
|
|
83
|
+
amm.quoteAssetReserve
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
newBudget = calculateRepegCost(amm, newOptimalPeg);
|
|
87
|
+
return [newTargetPrice, newOptimalPeg, newBudget, false];
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return [targetPrice, newPeg, budget, true];
|
|
92
|
+
}
|
|
93
|
+
|
|
28
94
|
export function calculateNewAmm(
|
|
29
95
|
amm: AMM,
|
|
30
96
|
oraclePriceData: OraclePriceData
|
|
@@ -32,18 +98,12 @@ export function calculateNewAmm(
|
|
|
32
98
|
let pKNumer = new BN(1);
|
|
33
99
|
let pKDenom = new BN(1);
|
|
34
100
|
|
|
35
|
-
const targetPrice =
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
.add(MARK_PRICE_PRECISION.div(PEG_PRECISION).div(new BN(2)))
|
|
40
|
-
.div(MARK_PRICE_PRECISION.div(PEG_PRECISION));
|
|
41
|
-
let prePegCost = calculateRepegCost(amm, newPeg);
|
|
42
|
-
|
|
43
|
-
const totalFeeLB = amm.totalExchangeFee.div(new BN(2));
|
|
44
|
-
const budget = BN.max(ZERO, amm.totalFeeMinusDistributions.sub(totalFeeLB));
|
|
101
|
+
const [targetPrice, _newPeg, budget, checkLowerBound] =
|
|
102
|
+
calculateOptimalPegAndBudget(amm, oraclePriceData);
|
|
103
|
+
let prePegCost = calculateRepegCost(amm, _newPeg);
|
|
104
|
+
let newPeg = _newPeg;
|
|
45
105
|
|
|
46
|
-
if (prePegCost.gt(budget)) {
|
|
106
|
+
if (prePegCost.gt(budget) && checkLowerBound) {
|
|
47
107
|
[pKNumer, pKDenom] = [new BN(999), new BN(1000)];
|
|
48
108
|
const deficitMadeup = calculateAdjustKCost(amm, pKNumer, pKDenom);
|
|
49
109
|
assert(deficitMadeup.lte(new BN(0)));
|
|
@@ -136,9 +196,16 @@ export function calculateUpdatedAMMSpreadReserves(
|
|
|
136
196
|
|
|
137
197
|
export function calculateBidAskPrice(
|
|
138
198
|
amm: AMM,
|
|
139
|
-
oraclePriceData: OraclePriceData
|
|
199
|
+
oraclePriceData: OraclePriceData,
|
|
200
|
+
withUpdate = true
|
|
140
201
|
): [BN, BN] {
|
|
141
|
-
|
|
202
|
+
let newAmm: AMM;
|
|
203
|
+
if (withUpdate) {
|
|
204
|
+
newAmm = calculateUpdatedAMM(amm, oraclePriceData);
|
|
205
|
+
} else {
|
|
206
|
+
newAmm = amm;
|
|
207
|
+
}
|
|
208
|
+
|
|
142
209
|
const askReserves = calculateSpreadReserves(
|
|
143
210
|
newAmm,
|
|
144
211
|
PositionDirection.LONG,
|
|
@@ -237,6 +304,108 @@ export function calculateAmmReservesAfterSwap(
|
|
|
237
304
|
return [newQuoteAssetReserve, newBaseAssetReserve];
|
|
238
305
|
}
|
|
239
306
|
|
|
307
|
+
export function calculateEffectiveLeverage(
|
|
308
|
+
baseSpread: number,
|
|
309
|
+
quoteAssetReserve: BN,
|
|
310
|
+
terminalQuoteAssetReserve: BN,
|
|
311
|
+
pegMultiplier: BN,
|
|
312
|
+
netBaseAssetAmount: BN,
|
|
313
|
+
markPrice: BN,
|
|
314
|
+
totalFeeMinusDistributions: BN
|
|
315
|
+
): number {
|
|
316
|
+
// inventory skew
|
|
317
|
+
const netBaseAssetValue = quoteAssetReserve
|
|
318
|
+
.sub(terminalQuoteAssetReserve)
|
|
319
|
+
.mul(pegMultiplier)
|
|
320
|
+
.div(AMM_TIMES_PEG_TO_QUOTE_PRECISION_RATIO);
|
|
321
|
+
|
|
322
|
+
const localBaseAssetValue = netBaseAssetAmount
|
|
323
|
+
.mul(markPrice)
|
|
324
|
+
.div(AMM_TO_QUOTE_PRECISION_RATIO.mul(MARK_PRICE_PRECISION));
|
|
325
|
+
|
|
326
|
+
const effectiveLeverage =
|
|
327
|
+
localBaseAssetValue.sub(netBaseAssetValue).toNumber() /
|
|
328
|
+
(Math.max(0, totalFeeMinusDistributions.toNumber()) + 1) +
|
|
329
|
+
1 / QUOTE_PRECISION.toNumber();
|
|
330
|
+
|
|
331
|
+
return effectiveLeverage;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
export function calculateMaxSpread(marginRatioInitial: number): number {
|
|
335
|
+
const maxTargetSpread: number = new BN(marginRatioInitial)
|
|
336
|
+
.mul(BID_ASK_SPREAD_PRECISION.div(MARGIN_PRECISION))
|
|
337
|
+
.toNumber();
|
|
338
|
+
|
|
339
|
+
return maxTargetSpread;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
export function calculateSpreadBN(
|
|
343
|
+
baseSpread: number,
|
|
344
|
+
lastOracleMarkSpreadPct: BN,
|
|
345
|
+
lastOracleConfPct: BN,
|
|
346
|
+
maxSpread: number,
|
|
347
|
+
quoteAssetReserve: BN,
|
|
348
|
+
terminalQuoteAssetReserve: BN,
|
|
349
|
+
pegMultiplier: BN,
|
|
350
|
+
netBaseAssetAmount: BN,
|
|
351
|
+
markPrice: BN,
|
|
352
|
+
totalFeeMinusDistributions: BN
|
|
353
|
+
): [number, number] {
|
|
354
|
+
let longSpread = baseSpread / 2;
|
|
355
|
+
let shortSpread = baseSpread / 2;
|
|
356
|
+
|
|
357
|
+
if (lastOracleMarkSpreadPct.gt(ZERO)) {
|
|
358
|
+
shortSpread = Math.max(
|
|
359
|
+
shortSpread,
|
|
360
|
+
lastOracleMarkSpreadPct.abs().toNumber() + lastOracleConfPct.toNumber()
|
|
361
|
+
);
|
|
362
|
+
} else if (lastOracleMarkSpreadPct.lt(ZERO)) {
|
|
363
|
+
longSpread = Math.max(
|
|
364
|
+
longSpread,
|
|
365
|
+
lastOracleMarkSpreadPct.abs().toNumber() + lastOracleConfPct.toNumber()
|
|
366
|
+
);
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
const maxTargetSpread: number = maxSpread;
|
|
370
|
+
|
|
371
|
+
const MAX_INVENTORY_SKEW = 5;
|
|
372
|
+
|
|
373
|
+
const effectiveLeverage = calculateEffectiveLeverage(
|
|
374
|
+
baseSpread,
|
|
375
|
+
quoteAssetReserve,
|
|
376
|
+
terminalQuoteAssetReserve,
|
|
377
|
+
pegMultiplier,
|
|
378
|
+
netBaseAssetAmount,
|
|
379
|
+
markPrice,
|
|
380
|
+
totalFeeMinusDistributions
|
|
381
|
+
);
|
|
382
|
+
|
|
383
|
+
if (totalFeeMinusDistributions.gt(ZERO)) {
|
|
384
|
+
const spreadScale = Math.min(MAX_INVENTORY_SKEW, 1 + effectiveLeverage);
|
|
385
|
+
if (netBaseAssetAmount.gt(ZERO)) {
|
|
386
|
+
longSpread *= spreadScale;
|
|
387
|
+
} else {
|
|
388
|
+
shortSpread *= spreadScale;
|
|
389
|
+
}
|
|
390
|
+
} else {
|
|
391
|
+
longSpread *= MAX_INVENTORY_SKEW;
|
|
392
|
+
shortSpread *= MAX_INVENTORY_SKEW;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
const totalSpread = longSpread + shortSpread;
|
|
396
|
+
if (totalSpread > maxTargetSpread) {
|
|
397
|
+
if (longSpread > shortSpread) {
|
|
398
|
+
longSpread = Math.min(longSpread, maxTargetSpread);
|
|
399
|
+
shortSpread = maxTargetSpread - longSpread;
|
|
400
|
+
} else {
|
|
401
|
+
shortSpread = Math.min(shortSpread, maxTargetSpread);
|
|
402
|
+
longSpread = maxTargetSpread - shortSpread;
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
return [longSpread, shortSpread];
|
|
407
|
+
}
|
|
408
|
+
|
|
240
409
|
export function calculateSpread(
|
|
241
410
|
amm: AMM,
|
|
242
411
|
direction: PositionDirection,
|
|
@@ -255,18 +424,26 @@ export function calculateSpread(
|
|
|
255
424
|
);
|
|
256
425
|
|
|
257
426
|
const targetPrice = oraclePriceData?.price || markPrice;
|
|
427
|
+
const confInterval = oraclePriceData.confidence || ZERO;
|
|
258
428
|
|
|
259
429
|
const targetMarkSpreadPct = markPrice
|
|
260
430
|
.sub(targetPrice)
|
|
261
431
|
.mul(BID_ASK_SPREAD_PRECISION)
|
|
262
432
|
.div(markPrice);
|
|
263
433
|
|
|
434
|
+
const confIntervalPct = confInterval
|
|
435
|
+
.mul(BID_ASK_SPREAD_PRECISION)
|
|
436
|
+
.div(markPrice);
|
|
437
|
+
|
|
264
438
|
// oracle retreat
|
|
265
439
|
if (
|
|
266
440
|
(isVariant(direction, 'long') && targetMarkSpreadPct.lt(ZERO)) ||
|
|
267
441
|
(isVariant(direction, 'short') && targetMarkSpreadPct.gt(ZERO))
|
|
268
442
|
) {
|
|
269
|
-
spread = Math.max(
|
|
443
|
+
spread = Math.max(
|
|
444
|
+
spread,
|
|
445
|
+
targetMarkSpreadPct.abs().toNumber() + confIntervalPct.abs().toNumber()
|
|
446
|
+
);
|
|
270
447
|
}
|
|
271
448
|
|
|
272
449
|
// inventory skew
|
|
@@ -294,10 +471,18 @@ export function calculateSpread(
|
|
|
294
471
|
if (amm.totalFeeMinusDistributions.gt(ZERO)) {
|
|
295
472
|
effectiveLeverage =
|
|
296
473
|
localPnl.sub(netPnl).toNumber() /
|
|
297
|
-
amm.totalFeeMinusDistributions.toNumber();
|
|
474
|
+
(amm.totalFeeMinusDistributions.toNumber() + 1);
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
let spreadScale = Math.min(MAX_INVENTORY_SKEW, 1 + effectiveLeverage);
|
|
478
|
+
const maxTargetSpread = BID_ASK_SPREAD_PRECISION.toNumber() / 50; // 2%
|
|
479
|
+
// cap the scale to attempt to only scale up to maxTargetSpread
|
|
480
|
+
// always let the oracle retreat methods go through 100%
|
|
481
|
+
if (spreadScale * spread > maxTargetSpread) {
|
|
482
|
+
spreadScale = Math.max(1.05, maxTargetSpread / spread);
|
|
298
483
|
}
|
|
299
484
|
|
|
300
|
-
spread *=
|
|
485
|
+
spread *= spreadScale;
|
|
301
486
|
}
|
|
302
487
|
|
|
303
488
|
return spread;
|
package/src/math/orders.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ClearingHouseUser } from '../clearingHouseUser';
|
|
2
|
-
import { isVariant, Order } from '../types';
|
|
2
|
+
import { isOneOfVariant, isVariant, Order } from '../types';
|
|
3
3
|
import { ZERO, TWO } from '../constants/numericConstants';
|
|
4
4
|
import { BN } from '@project-serum/anchor';
|
|
5
5
|
import { OraclePriceData } from '../oracles/types';
|
|
@@ -125,18 +125,8 @@ export function getLimitPrice(
|
|
|
125
125
|
): BN {
|
|
126
126
|
let limitPrice;
|
|
127
127
|
if (!order.oraclePriceOffset.eq(ZERO)) {
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
limitPrice = isVariant(order.direction, 'long')
|
|
131
|
-
? BN.min(order.price, floatingPrice)
|
|
132
|
-
: BN.max(order.price, floatingPrice);
|
|
133
|
-
} else {
|
|
134
|
-
limitPrice = floatingPrice;
|
|
135
|
-
}
|
|
136
|
-
} else if (
|
|
137
|
-
isVariant(order.orderType, 'market') ||
|
|
138
|
-
isVariant(order.orderType, 'triggerMarket')
|
|
139
|
-
) {
|
|
128
|
+
limitPrice = oraclePriceData.price.add(order.oraclePriceOffset);
|
|
129
|
+
} else if (isOneOfVariant(order.orderType, ['market', 'triggerMarket'])) {
|
|
140
130
|
limitPrice = getAuctionPrice(order, slot);
|
|
141
131
|
} else {
|
|
142
132
|
limitPrice = order.price;
|
package/src/math/repeg.ts
CHANGED
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
AMM_RESERVE_PRECISION,
|
|
6
6
|
PEG_PRECISION,
|
|
7
7
|
AMM_TO_QUOTE_PRECISION_RATIO,
|
|
8
|
+
PRICE_DIV_PEG,
|
|
8
9
|
QUOTE_PRECISION,
|
|
9
10
|
ZERO,
|
|
10
11
|
} from '../constants/numericConstants';
|
|
@@ -142,7 +143,7 @@ export function calculateBudgetedPeg(amm: AMM, cost: BN, targetPrice: BN): BN {
|
|
|
142
143
|
const targetPeg = targetPrice
|
|
143
144
|
.mul(amm.baseAssetReserve)
|
|
144
145
|
.div(amm.quoteAssetReserve)
|
|
145
|
-
.div(
|
|
146
|
+
.div(PRICE_DIV_PEG);
|
|
146
147
|
|
|
147
148
|
const k = amm.sqrtK.mul(amm.sqrtK);
|
|
148
149
|
const x = amm.baseAssetReserve;
|
package/src/orders.ts
CHANGED
|
@@ -3,27 +3,13 @@ import {
|
|
|
3
3
|
MarketAccount,
|
|
4
4
|
Order,
|
|
5
5
|
PositionDirection,
|
|
6
|
-
SwapDirection,
|
|
7
6
|
UserAccount,
|
|
8
7
|
UserPosition,
|
|
9
8
|
} from './types';
|
|
10
|
-
import {
|
|
11
|
-
|
|
12
|
-
calculateAmmReservesAfterSwap,
|
|
13
|
-
calculateBaseAssetValue,
|
|
14
|
-
calculateSpreadReserves,
|
|
15
|
-
ClearingHouseUser,
|
|
16
|
-
isOrderRiskIncreasingInSameDirection,
|
|
17
|
-
standardizeBaseAssetAmount,
|
|
18
|
-
TEN_THOUSAND,
|
|
19
|
-
} from '.';
|
|
20
|
-
import {
|
|
21
|
-
calculateMarkPrice,
|
|
22
|
-
calculateNewMarketAfterTrade,
|
|
23
|
-
} from './math/market';
|
|
9
|
+
import { BN, standardizeBaseAssetAmount } from '.';
|
|
10
|
+
import { calculateNewMarketAfterTrade } from './math/market';
|
|
24
11
|
import {
|
|
25
12
|
AMM_TO_QUOTE_PRECISION_RATIO,
|
|
26
|
-
TWO,
|
|
27
13
|
PEG_PRECISION,
|
|
28
14
|
ZERO,
|
|
29
15
|
} from './constants/numericConstants';
|
|
@@ -182,7 +168,6 @@ export function calculateBaseAssetAmountMarketCanExecute(
|
|
|
182
168
|
} else if (isVariant(order.orderType, 'triggerLimit')) {
|
|
183
169
|
return calculateAmountToTradeForTriggerLimit(market, order);
|
|
184
170
|
} else if (isVariant(order.orderType, 'market')) {
|
|
185
|
-
// should never be a market order queued
|
|
186
171
|
return ZERO;
|
|
187
172
|
} else {
|
|
188
173
|
return calculateAmountToTradeForTriggerMarket(market, order);
|
|
@@ -201,14 +186,7 @@ export function calculateAmountToTradeForLimit(
|
|
|
201
186
|
'Cant calculate limit price for oracle offset oracle without OraclePriceData'
|
|
202
187
|
);
|
|
203
188
|
}
|
|
204
|
-
|
|
205
|
-
if (order.postOnly) {
|
|
206
|
-
limitPrice = isVariant(order.direction, 'long')
|
|
207
|
-
? BN.min(order.price, floatingPrice)
|
|
208
|
-
: BN.max(order.price, floatingPrice);
|
|
209
|
-
} else {
|
|
210
|
-
limitPrice = floatingPrice;
|
|
211
|
-
}
|
|
189
|
+
limitPrice = oraclePriceData.price.add(order.oraclePriceOffset);
|
|
212
190
|
}
|
|
213
191
|
|
|
214
192
|
const [maxAmountToTrade, direction] = calculateMaxBaseAssetAmountToTrade(
|
|
@@ -237,14 +215,8 @@ export function calculateAmountToTradeForTriggerLimit(
|
|
|
237
215
|
market: MarketAccount,
|
|
238
216
|
order: Order
|
|
239
217
|
): BN {
|
|
240
|
-
if (order.
|
|
241
|
-
|
|
242
|
-
market,
|
|
243
|
-
order
|
|
244
|
-
);
|
|
245
|
-
if (baseAssetAmount.eq(ZERO)) {
|
|
246
|
-
return ZERO;
|
|
247
|
-
}
|
|
218
|
+
if (!order.triggered) {
|
|
219
|
+
return ZERO;
|
|
248
220
|
}
|
|
249
221
|
|
|
250
222
|
return calculateAmountToTradeForLimit(market, order);
|
|
@@ -264,105 +236,9 @@ function calculateAmountToTradeForTriggerMarket(
|
|
|
264
236
|
market: MarketAccount,
|
|
265
237
|
order: Order
|
|
266
238
|
): BN {
|
|
267
|
-
|
|
268
|
-
? order.baseAssetAmount
|
|
269
|
-
: ZERO;
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
function isTriggerConditionSatisfied(
|
|
273
|
-
market: MarketAccount,
|
|
274
|
-
order: Order,
|
|
275
|
-
oraclePriceData?: OraclePriceData
|
|
276
|
-
): boolean {
|
|
277
|
-
const markPrice = calculateMarkPrice(market, oraclePriceData);
|
|
278
|
-
if (isVariant(order.triggerCondition, 'above')) {
|
|
279
|
-
return markPrice.gt(order.triggerPrice);
|
|
280
|
-
} else {
|
|
281
|
-
return markPrice.lt(order.triggerPrice);
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
export function calculateBaseAssetAmountUserCanExecute(
|
|
286
|
-
market: MarketAccount,
|
|
287
|
-
order: Order,
|
|
288
|
-
user: ClearingHouseUser,
|
|
289
|
-
oraclePriceData?: OraclePriceData
|
|
290
|
-
): BN {
|
|
291
|
-
const maxLeverage = user.getMaxLeverage(order.marketIndex, 'Initial');
|
|
292
|
-
const freeCollateral = user.getFreeCollateral();
|
|
293
|
-
let quoteAssetAmount: BN;
|
|
294
|
-
if (isOrderRiskIncreasingInSameDirection(user, order)) {
|
|
295
|
-
quoteAssetAmount = freeCollateral.mul(maxLeverage).div(TEN_THOUSAND);
|
|
296
|
-
} else {
|
|
297
|
-
const position =
|
|
298
|
-
user.getUserPosition(order.marketIndex) ||
|
|
299
|
-
user.getEmptyPosition(order.marketIndex);
|
|
300
|
-
const positionValue = calculateBaseAssetValue(
|
|
301
|
-
market,
|
|
302
|
-
position,
|
|
303
|
-
oraclePriceData
|
|
304
|
-
);
|
|
305
|
-
quoteAssetAmount = freeCollateral
|
|
306
|
-
.mul(maxLeverage)
|
|
307
|
-
.div(TEN_THOUSAND)
|
|
308
|
-
.add(positionValue.mul(TWO));
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
if (quoteAssetAmount.lte(ZERO)) {
|
|
239
|
+
if (!order.triggered) {
|
|
312
240
|
return ZERO;
|
|
313
241
|
}
|
|
314
242
|
|
|
315
|
-
|
|
316
|
-
? SwapDirection.ADD
|
|
317
|
-
: SwapDirection.REMOVE;
|
|
318
|
-
|
|
319
|
-
const useSpread = !order.postOnly;
|
|
320
|
-
let amm: Parameters<typeof calculateAmmReservesAfterSwap>[0];
|
|
321
|
-
if (useSpread) {
|
|
322
|
-
const { baseAssetReserve, quoteAssetReserve } = calculateSpreadReserves(
|
|
323
|
-
market.amm,
|
|
324
|
-
order.direction,
|
|
325
|
-
oraclePriceData
|
|
326
|
-
);
|
|
327
|
-
amm = {
|
|
328
|
-
baseAssetReserve,
|
|
329
|
-
quoteAssetReserve,
|
|
330
|
-
sqrtK: market.amm.sqrtK,
|
|
331
|
-
pegMultiplier: market.amm.pegMultiplier,
|
|
332
|
-
};
|
|
333
|
-
} else {
|
|
334
|
-
amm = market.amm;
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
const baseAssetReservesBefore = amm.baseAssetReserve;
|
|
338
|
-
const [_, baseAssetReservesAfter] = calculateAmmReservesAfterSwap(
|
|
339
|
-
amm,
|
|
340
|
-
'quote',
|
|
341
|
-
quoteAssetAmount,
|
|
342
|
-
swapDirection
|
|
343
|
-
);
|
|
344
|
-
|
|
345
|
-
let baseAssetAmount = baseAssetReservesBefore
|
|
346
|
-
.sub(baseAssetReservesAfter)
|
|
347
|
-
.abs();
|
|
348
|
-
if (order.reduceOnly) {
|
|
349
|
-
const position =
|
|
350
|
-
user.getUserPosition(order.marketIndex) ||
|
|
351
|
-
user.getEmptyPosition(order.marketIndex);
|
|
352
|
-
if (
|
|
353
|
-
isVariant(order.direction, 'long') &&
|
|
354
|
-
position.baseAssetAmount.gte(ZERO)
|
|
355
|
-
) {
|
|
356
|
-
baseAssetAmount = ZERO;
|
|
357
|
-
} else if (
|
|
358
|
-
isVariant(order.direction, 'short') &&
|
|
359
|
-
position.baseAssetAmount.lte(ZERO)
|
|
360
|
-
) {
|
|
361
|
-
baseAssetAmount = ZERO;
|
|
362
|
-
} else {
|
|
363
|
-
BN.min(baseAssetAmount, position.baseAssetAmount.abs());
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
return baseAssetAmount;
|
|
243
|
+
return order.baseAssetAmount;
|
|
368
244
|
}
|
package/src/types.ts
CHANGED
|
@@ -48,6 +48,7 @@ export class OrderAction {
|
|
|
48
48
|
static readonly CANCEL = { cancel: {} };
|
|
49
49
|
static readonly EXPIRE = { expire: {} };
|
|
50
50
|
static readonly FILL = { fill: {} };
|
|
51
|
+
static readonly TRIGGER = { trigger: {} };
|
|
51
52
|
}
|
|
52
53
|
|
|
53
54
|
export class OrderTriggerCondition {
|
|
@@ -290,6 +291,7 @@ export type AMM = {
|
|
|
290
291
|
lastAskPriceTwap: BN;
|
|
291
292
|
longSpread: BN;
|
|
292
293
|
shortSpread: BN;
|
|
294
|
+
maxSpread: number;
|
|
293
295
|
};
|
|
294
296
|
|
|
295
297
|
// # User Account Types
|
|
@@ -347,8 +349,9 @@ export type Order = {
|
|
|
347
349
|
reduceOnly: boolean;
|
|
348
350
|
triggerPrice: BN;
|
|
349
351
|
triggerCondition: OrderTriggerCondition;
|
|
352
|
+
triggered: boolean;
|
|
350
353
|
discountTier: OrderDiscountTier;
|
|
351
|
-
existingPositionDirection: PositionDirection
|
|
354
|
+
existingPositionDirection: PositionDirection;
|
|
352
355
|
referrer: PublicKey;
|
|
353
356
|
postOnly: boolean;
|
|
354
357
|
immediateOrCancel: boolean;
|