@drift-labs/sdk 0.2.0-master.1 → 0.2.0-master.2
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/accounts/types.d.ts +1 -0
- package/lib/admin.d.ts +1 -0
- package/lib/admin.js +9 -0
- package/lib/clearingHouse.d.ts +5 -3
- package/lib/clearingHouse.js +58 -4
- 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 -19
- package/lib/math/amm.d.ts +6 -1
- package/lib/math/amm.js +127 -16
- package/lib/math/repeg.js +1 -1
- package/lib/orders.d.ts +1 -2
- package/lib/orders.js +5 -76
- package/lib/types.d.ts +5 -0
- package/lib/types.js +1 -0
- package/package.json +1 -1
- package/src/admin.ts +13 -0
- package/src/clearingHouse.ts +82 -8
- 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 -19
- package/src/math/amm.ts +202 -17
- package/src/math/repeg.ts +2 -1
- package/src/orders.ts +6 -123
- package/src/types.ts +4 -1
|
@@ -414,17 +414,14 @@
|
|
|
414
414
|
"name": "authority",
|
|
415
415
|
"isMut": false,
|
|
416
416
|
"isSigner": true
|
|
417
|
-
},
|
|
418
|
-
{
|
|
419
|
-
"name": "oracle",
|
|
420
|
-
"isMut": false,
|
|
421
|
-
"isSigner": false
|
|
422
417
|
}
|
|
423
418
|
],
|
|
424
419
|
"args": [
|
|
425
420
|
{
|
|
426
421
|
"name": "orderId",
|
|
427
|
-
"type":
|
|
422
|
+
"type": {
|
|
423
|
+
"option": "u64"
|
|
424
|
+
}
|
|
428
425
|
}
|
|
429
426
|
]
|
|
430
427
|
},
|
|
@@ -445,11 +442,6 @@
|
|
|
445
442
|
"name": "authority",
|
|
446
443
|
"isMut": false,
|
|
447
444
|
"isSigner": true
|
|
448
|
-
},
|
|
449
|
-
{
|
|
450
|
-
"name": "oracle",
|
|
451
|
-
"isMut": false,
|
|
452
|
-
"isSigner": false
|
|
453
445
|
}
|
|
454
446
|
],
|
|
455
447
|
"args": [
|
|
@@ -491,7 +483,9 @@
|
|
|
491
483
|
"args": [
|
|
492
484
|
{
|
|
493
485
|
"name": "orderId",
|
|
494
|
-
"type":
|
|
486
|
+
"type": {
|
|
487
|
+
"option": "u64"
|
|
488
|
+
}
|
|
495
489
|
},
|
|
496
490
|
{
|
|
497
491
|
"name": "makerOrderId",
|
|
@@ -582,6 +576,37 @@
|
|
|
582
576
|
}
|
|
583
577
|
]
|
|
584
578
|
},
|
|
579
|
+
{
|
|
580
|
+
"name": "triggerOrder",
|
|
581
|
+
"accounts": [
|
|
582
|
+
{
|
|
583
|
+
"name": "state",
|
|
584
|
+
"isMut": false,
|
|
585
|
+
"isSigner": false
|
|
586
|
+
},
|
|
587
|
+
{
|
|
588
|
+
"name": "authority",
|
|
589
|
+
"isMut": false,
|
|
590
|
+
"isSigner": true
|
|
591
|
+
},
|
|
592
|
+
{
|
|
593
|
+
"name": "filler",
|
|
594
|
+
"isMut": true,
|
|
595
|
+
"isSigner": false
|
|
596
|
+
},
|
|
597
|
+
{
|
|
598
|
+
"name": "user",
|
|
599
|
+
"isMut": true,
|
|
600
|
+
"isSigner": false
|
|
601
|
+
}
|
|
602
|
+
],
|
|
603
|
+
"args": [
|
|
604
|
+
{
|
|
605
|
+
"name": "orderId",
|
|
606
|
+
"type": "u64"
|
|
607
|
+
}
|
|
608
|
+
]
|
|
609
|
+
},
|
|
585
610
|
{
|
|
586
611
|
"name": "updateAmms",
|
|
587
612
|
"accounts": [
|
|
@@ -993,11 +1018,6 @@
|
|
|
993
1018
|
"name": "user",
|
|
994
1019
|
"isMut": true,
|
|
995
1020
|
"isSigner": false
|
|
996
|
-
},
|
|
997
|
-
{
|
|
998
|
-
"name": "market",
|
|
999
|
-
"isMut": false,
|
|
1000
|
-
"isSigner": false
|
|
1001
1021
|
}
|
|
1002
1022
|
],
|
|
1003
1023
|
"args": []
|
|
@@ -1389,6 +1409,32 @@
|
|
|
1389
1409
|
}
|
|
1390
1410
|
]
|
|
1391
1411
|
},
|
|
1412
|
+
{
|
|
1413
|
+
"name": "updateMarketMaxSpread",
|
|
1414
|
+
"accounts": [
|
|
1415
|
+
{
|
|
1416
|
+
"name": "admin",
|
|
1417
|
+
"isMut": false,
|
|
1418
|
+
"isSigner": true
|
|
1419
|
+
},
|
|
1420
|
+
{
|
|
1421
|
+
"name": "state",
|
|
1422
|
+
"isMut": false,
|
|
1423
|
+
"isSigner": false
|
|
1424
|
+
},
|
|
1425
|
+
{
|
|
1426
|
+
"name": "market",
|
|
1427
|
+
"isMut": true,
|
|
1428
|
+
"isSigner": false
|
|
1429
|
+
}
|
|
1430
|
+
],
|
|
1431
|
+
"args": [
|
|
1432
|
+
{
|
|
1433
|
+
"name": "maxSpread",
|
|
1434
|
+
"type": "u32"
|
|
1435
|
+
}
|
|
1436
|
+
]
|
|
1437
|
+
},
|
|
1392
1438
|
{
|
|
1393
1439
|
"name": "updateMarketBaseAssetAmountStepSize",
|
|
1394
1440
|
"accounts": [
|
|
@@ -2194,6 +2240,10 @@
|
|
|
2194
2240
|
"name": "shortSpread",
|
|
2195
2241
|
"type": "u128"
|
|
2196
2242
|
},
|
|
2243
|
+
{
|
|
2244
|
+
"name": "maxSpread",
|
|
2245
|
+
"type": "u32"
|
|
2246
|
+
},
|
|
2197
2247
|
{
|
|
2198
2248
|
"name": "askBaseAssetReserve",
|
|
2199
2249
|
"type": "u128"
|
|
@@ -2252,7 +2302,7 @@
|
|
|
2252
2302
|
},
|
|
2253
2303
|
{
|
|
2254
2304
|
"name": "totalFeeMinusDistributions",
|
|
2255
|
-
"type": "
|
|
2305
|
+
"type": "i128"
|
|
2256
2306
|
},
|
|
2257
2307
|
{
|
|
2258
2308
|
"name": "totalFeeWithdrawn",
|
|
@@ -2712,6 +2762,10 @@
|
|
|
2712
2762
|
"defined": "OrderTriggerCondition"
|
|
2713
2763
|
}
|
|
2714
2764
|
},
|
|
2765
|
+
{
|
|
2766
|
+
"name": "triggered",
|
|
2767
|
+
"type": "bool"
|
|
2768
|
+
},
|
|
2715
2769
|
{
|
|
2716
2770
|
"name": "referrer",
|
|
2717
2771
|
"type": "publicKey"
|
|
@@ -2848,6 +2902,9 @@
|
|
|
2848
2902
|
{
|
|
2849
2903
|
"name": "Fill"
|
|
2850
2904
|
},
|
|
2905
|
+
{
|
|
2906
|
+
"name": "Trigger"
|
|
2907
|
+
},
|
|
2851
2908
|
{
|
|
2852
2909
|
"name": "Expire"
|
|
2853
2910
|
}
|
|
@@ -3016,6 +3073,11 @@
|
|
|
3016
3073
|
"type": "u64",
|
|
3017
3074
|
"index": false
|
|
3018
3075
|
},
|
|
3076
|
+
{
|
|
3077
|
+
"name": "oraclePrice",
|
|
3078
|
+
"type": "i128",
|
|
3079
|
+
"index": false
|
|
3080
|
+
},
|
|
3019
3081
|
{
|
|
3020
3082
|
"name": "from",
|
|
3021
3083
|
"type": {
|
|
@@ -3217,7 +3279,7 @@
|
|
|
3217
3279
|
},
|
|
3218
3280
|
{
|
|
3219
3281
|
"name": "totalFeeMinusDistributions",
|
|
3220
|
-
"type": "
|
|
3282
|
+
"type": "i128",
|
|
3221
3283
|
"index": false
|
|
3222
3284
|
},
|
|
3223
3285
|
{
|
|
@@ -3349,6 +3411,16 @@
|
|
|
3349
3411
|
},
|
|
3350
3412
|
"index": false
|
|
3351
3413
|
},
|
|
3414
|
+
{
|
|
3415
|
+
"name": "makerUnsettledPnl",
|
|
3416
|
+
"type": "i128",
|
|
3417
|
+
"index": false
|
|
3418
|
+
},
|
|
3419
|
+
{
|
|
3420
|
+
"name": "takerUnsettledPnl",
|
|
3421
|
+
"type": "i128",
|
|
3422
|
+
"index": false
|
|
3423
|
+
},
|
|
3352
3424
|
{
|
|
3353
3425
|
"name": "action",
|
|
3354
3426
|
"type": {
|
|
@@ -3854,6 +3926,21 @@
|
|
|
3854
3926
|
},
|
|
3855
3927
|
{
|
|
3856
3928
|
"code": 6087,
|
|
3929
|
+
"name": "OrderMustBeTriggeredFirst",
|
|
3930
|
+
"msg": "OrderMustBeTriggeredFirst"
|
|
3931
|
+
},
|
|
3932
|
+
{
|
|
3933
|
+
"code": 6088,
|
|
3934
|
+
"name": "OrderNotTriggerable",
|
|
3935
|
+
"msg": "OrderNotTriggerable"
|
|
3936
|
+
},
|
|
3937
|
+
{
|
|
3938
|
+
"code": 6089,
|
|
3939
|
+
"name": "OrderDidNotSatisfyTriggerCondition",
|
|
3940
|
+
"msg": "OrderDidNotSatisfyTriggerCondition"
|
|
3941
|
+
},
|
|
3942
|
+
{
|
|
3943
|
+
"code": 6090,
|
|
3857
3944
|
"name": "DefaultError",
|
|
3858
3945
|
"msg": "DefaultError"
|
|
3859
3946
|
}
|
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/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);
|
|
@@ -237,14 +222,8 @@ export function calculateAmountToTradeForTriggerLimit(
|
|
|
237
222
|
market: MarketAccount,
|
|
238
223
|
order: Order
|
|
239
224
|
): BN {
|
|
240
|
-
if (order.
|
|
241
|
-
|
|
242
|
-
market,
|
|
243
|
-
order
|
|
244
|
-
);
|
|
245
|
-
if (baseAssetAmount.eq(ZERO)) {
|
|
246
|
-
return ZERO;
|
|
247
|
-
}
|
|
225
|
+
if (!order.triggered) {
|
|
226
|
+
return ZERO;
|
|
248
227
|
}
|
|
249
228
|
|
|
250
229
|
return calculateAmountToTradeForLimit(market, order);
|
|
@@ -264,105 +243,9 @@ function calculateAmountToTradeForTriggerMarket(
|
|
|
264
243
|
market: MarketAccount,
|
|
265
244
|
order: Order
|
|
266
245
|
): 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)) {
|
|
246
|
+
if (!order.triggered) {
|
|
312
247
|
return ZERO;
|
|
313
248
|
}
|
|
314
249
|
|
|
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;
|
|
250
|
+
return order.baseAssetAmount;
|
|
368
251
|
}
|
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;
|