@drift-labs/sdk 2.0.13 → 2.0.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/README.md +3 -3
- package/lib/accounts/pollingDriftClientAccountSubscriber.js +0 -3
- package/lib/accounts/pollingUserStatsAccountSubscriber.js +0 -3
- package/lib/adminClient.d.ts +2 -0
- package/lib/adminClient.js +16 -0
- package/lib/constants/numericConstants.d.ts +2 -0
- package/lib/constants/numericConstants.js +3 -1
- package/lib/dlob/DLOB.d.ts +8 -8
- package/lib/dlob/DLOB.js +227 -246
- package/lib/driftClient.d.ts +19 -0
- package/lib/driftClient.js +32 -0
- package/lib/idl/drift.json +65 -7
- package/lib/math/amm.d.ts +3 -2
- package/lib/math/amm.js +61 -27
- package/lib/math/oracles.d.ts +2 -0
- package/lib/math/oracles.js +23 -1
- package/lib/math/orders.d.ts +1 -2
- package/lib/math/orders.js +4 -20
- package/lib/math/utils.d.ts +1 -0
- package/lib/math/utils.js +5 -1
- package/lib/types.d.ts +6 -2
- package/lib/user.js +10 -6
- package/package.json +1 -1
- package/src/accounts/pollingDriftClientAccountSubscriber.ts +0 -4
- package/src/accounts/pollingUserStatsAccountSubscriber.ts +0 -4
- package/src/adminClient.ts +28 -0
- package/src/constants/numericConstants.ts +3 -0
- package/src/dlob/DLOB.ts +444 -345
- package/src/driftClient.ts +41 -0
- package/src/idl/drift.json +65 -7
- package/src/math/amm.ts +126 -42
- package/src/math/oracles.ts +52 -0
- package/src/math/orders.ts +5 -22
- package/src/math/utils.ts +4 -0
- package/src/types.ts +6 -2
- package/src/user.ts +17 -6
- package/tests/dlob/helpers.ts +4 -2
- package/tests/dlob/test.ts +547 -63
package/src/driftClient.ts
CHANGED
|
@@ -300,12 +300,31 @@ export class DriftClient {
|
|
|
300
300
|
return this.accountSubscriber.getStateAccountAndSlot().data;
|
|
301
301
|
}
|
|
302
302
|
|
|
303
|
+
/**
|
|
304
|
+
* Forces a fetch to rpc before returning accounts. Useful for anchor tests.
|
|
305
|
+
*/
|
|
306
|
+
public async forceGetStateAccount(): Promise<StateAccount> {
|
|
307
|
+
await this.accountSubscriber.fetch();
|
|
308
|
+
return this.accountSubscriber.getStateAccountAndSlot().data;
|
|
309
|
+
}
|
|
310
|
+
|
|
303
311
|
public getPerpMarketAccount(
|
|
304
312
|
marketIndex: number
|
|
305
313
|
): PerpMarketAccount | undefined {
|
|
306
314
|
return this.accountSubscriber.getMarketAccountAndSlot(marketIndex)?.data;
|
|
307
315
|
}
|
|
308
316
|
|
|
317
|
+
/**
|
|
318
|
+
* Forces a fetch to rpc before returning accounts. Useful for anchor tests.
|
|
319
|
+
* @param marketIndex
|
|
320
|
+
*/
|
|
321
|
+
public async forceGetPerpMarketAccount(
|
|
322
|
+
marketIndex: number
|
|
323
|
+
): Promise<PerpMarketAccount | undefined> {
|
|
324
|
+
await this.accountSubscriber.fetch();
|
|
325
|
+
return this.accountSubscriber.getMarketAccountAndSlot(marketIndex)?.data;
|
|
326
|
+
}
|
|
327
|
+
|
|
309
328
|
public getPerpMarketAccounts(): PerpMarketAccount[] {
|
|
310
329
|
return this.accountSubscriber
|
|
311
330
|
.getMarketAccountsAndSlots()
|
|
@@ -318,6 +337,17 @@ export class DriftClient {
|
|
|
318
337
|
return this.accountSubscriber.getSpotMarketAccountAndSlot(marketIndex).data;
|
|
319
338
|
}
|
|
320
339
|
|
|
340
|
+
/**
|
|
341
|
+
* Forces a fetch to rpc before returning accounts. Useful for anchor tests.
|
|
342
|
+
* @param marketIndex
|
|
343
|
+
*/
|
|
344
|
+
public async forceGetSpotMarketAccount(
|
|
345
|
+
marketIndex: number
|
|
346
|
+
): Promise<SpotMarketAccount | undefined> {
|
|
347
|
+
await this.accountSubscriber.fetch();
|
|
348
|
+
return this.accountSubscriber.getSpotMarketAccountAndSlot(marketIndex).data;
|
|
349
|
+
}
|
|
350
|
+
|
|
321
351
|
public getSpotMarketAccounts(): SpotMarketAccount[] {
|
|
322
352
|
return this.accountSubscriber
|
|
323
353
|
.getSpotMarketAccountsAndSlots()
|
|
@@ -679,6 +709,17 @@ export class DriftClient {
|
|
|
679
709
|
return this.getUser(subAccountId).getUserAccount();
|
|
680
710
|
}
|
|
681
711
|
|
|
712
|
+
/**
|
|
713
|
+
* Forces a fetch to rpc before returning accounts. Useful for anchor tests.
|
|
714
|
+
* @param subAccountId
|
|
715
|
+
*/
|
|
716
|
+
public async forceGetUserAccount(
|
|
717
|
+
subAccountId?: number
|
|
718
|
+
): Promise<UserAccount | undefined> {
|
|
719
|
+
await this.getUser(subAccountId).fetchAccounts();
|
|
720
|
+
return this.getUser(subAccountId).getUserAccount();
|
|
721
|
+
}
|
|
722
|
+
|
|
682
723
|
public getUserAccountAndSlot(
|
|
683
724
|
subAccountId?: number
|
|
684
725
|
): DataAndSlot<UserAccount> | undefined {
|
package/src/idl/drift.json
CHANGED
|
@@ -1432,7 +1432,7 @@
|
|
|
1432
1432
|
},
|
|
1433
1433
|
{
|
|
1434
1434
|
"name": "spotMarket",
|
|
1435
|
-
"isMut":
|
|
1435
|
+
"isMut": true,
|
|
1436
1436
|
"isSigner": false
|
|
1437
1437
|
},
|
|
1438
1438
|
{
|
|
@@ -3233,6 +3233,48 @@
|
|
|
3233
3233
|
}
|
|
3234
3234
|
]
|
|
3235
3235
|
},
|
|
3236
|
+
{
|
|
3237
|
+
"name": "updateInitialPctToLiquidate",
|
|
3238
|
+
"accounts": [
|
|
3239
|
+
{
|
|
3240
|
+
"name": "admin",
|
|
3241
|
+
"isMut": false,
|
|
3242
|
+
"isSigner": true
|
|
3243
|
+
},
|
|
3244
|
+
{
|
|
3245
|
+
"name": "state",
|
|
3246
|
+
"isMut": true,
|
|
3247
|
+
"isSigner": false
|
|
3248
|
+
}
|
|
3249
|
+
],
|
|
3250
|
+
"args": [
|
|
3251
|
+
{
|
|
3252
|
+
"name": "initialPctToLiquidate",
|
|
3253
|
+
"type": "u16"
|
|
3254
|
+
}
|
|
3255
|
+
]
|
|
3256
|
+
},
|
|
3257
|
+
{
|
|
3258
|
+
"name": "updateLiquidationDuration",
|
|
3259
|
+
"accounts": [
|
|
3260
|
+
{
|
|
3261
|
+
"name": "admin",
|
|
3262
|
+
"isMut": false,
|
|
3263
|
+
"isSigner": true
|
|
3264
|
+
},
|
|
3265
|
+
{
|
|
3266
|
+
"name": "state",
|
|
3267
|
+
"isMut": true,
|
|
3268
|
+
"isSigner": false
|
|
3269
|
+
}
|
|
3270
|
+
],
|
|
3271
|
+
"args": [
|
|
3272
|
+
{
|
|
3273
|
+
"name": "liquidationDuration",
|
|
3274
|
+
"type": "u8"
|
|
3275
|
+
}
|
|
3276
|
+
]
|
|
3277
|
+
},
|
|
3236
3278
|
{
|
|
3237
3279
|
"name": "updateOracleGuardRails",
|
|
3238
3280
|
"accounts": [
|
|
@@ -4320,12 +4362,20 @@
|
|
|
4320
4362
|
"defined": "ExchangeStatus"
|
|
4321
4363
|
}
|
|
4322
4364
|
},
|
|
4365
|
+
{
|
|
4366
|
+
"name": "liquidationDuration",
|
|
4367
|
+
"type": "u8"
|
|
4368
|
+
},
|
|
4369
|
+
{
|
|
4370
|
+
"name": "initialPctToLiquidate",
|
|
4371
|
+
"type": "u16"
|
|
4372
|
+
},
|
|
4323
4373
|
{
|
|
4324
4374
|
"name": "padding",
|
|
4325
4375
|
"type": {
|
|
4326
4376
|
"array": [
|
|
4327
4377
|
"u8",
|
|
4328
|
-
|
|
4378
|
+
14
|
|
4329
4379
|
]
|
|
4330
4380
|
}
|
|
4331
4381
|
}
|
|
@@ -4420,8 +4470,8 @@
|
|
|
4420
4470
|
"type": "u64"
|
|
4421
4471
|
},
|
|
4422
4472
|
{
|
|
4423
|
-
"name": "
|
|
4424
|
-
"type": "
|
|
4473
|
+
"name": "liquidationStartSlot",
|
|
4474
|
+
"type": "u64"
|
|
4425
4475
|
},
|
|
4426
4476
|
{
|
|
4427
4477
|
"name": "nextOrderId",
|
|
@@ -5596,11 +5646,11 @@
|
|
|
5596
5646
|
"type": "u64"
|
|
5597
5647
|
},
|
|
5598
5648
|
{
|
|
5599
|
-
"name": "
|
|
5649
|
+
"name": "lastBaseAssetAmountPerLp",
|
|
5600
5650
|
"type": "i64"
|
|
5601
5651
|
},
|
|
5602
5652
|
{
|
|
5603
|
-
"name": "
|
|
5653
|
+
"name": "lastQuoteAssetAmountPerLp",
|
|
5604
5654
|
"type": "i64"
|
|
5605
5655
|
},
|
|
5606
5656
|
{
|
|
@@ -6016,6 +6066,9 @@
|
|
|
6016
6066
|
},
|
|
6017
6067
|
{
|
|
6018
6068
|
"name": "OrderFillWithSerum"
|
|
6069
|
+
},
|
|
6070
|
+
{
|
|
6071
|
+
"name": "NoBorrowLiquidity"
|
|
6019
6072
|
}
|
|
6020
6073
|
]
|
|
6021
6074
|
}
|
|
@@ -8454,11 +8507,16 @@
|
|
|
8454
8507
|
},
|
|
8455
8508
|
{
|
|
8456
8509
|
"code": 6219,
|
|
8510
|
+
"name": "UnableToGetLimitPrice",
|
|
8511
|
+
"msg": "Unable To Get Limit Price"
|
|
8512
|
+
},
|
|
8513
|
+
{
|
|
8514
|
+
"code": 6220,
|
|
8457
8515
|
"name": "InvalidLiquidation",
|
|
8458
8516
|
"msg": "Invalid Liquidation"
|
|
8459
8517
|
},
|
|
8460
8518
|
{
|
|
8461
|
-
"code":
|
|
8519
|
+
"code": 6221,
|
|
8462
8520
|
"name": "SpotFulfillmentConfigDisabled",
|
|
8463
8521
|
"msg": "Spot Fulfullment Config Disabled"
|
|
8464
8522
|
}
|
package/src/math/amm.ts
CHANGED
|
@@ -10,6 +10,8 @@ import {
|
|
|
10
10
|
QUOTE_PRECISION,
|
|
11
11
|
MARGIN_PRECISION,
|
|
12
12
|
PRICE_DIV_PEG,
|
|
13
|
+
PERCENTAGE_PRECISION,
|
|
14
|
+
BASE_PRECISION,
|
|
13
15
|
} from '../constants/numericConstants';
|
|
14
16
|
import {
|
|
15
17
|
AMM,
|
|
@@ -19,7 +21,7 @@ import {
|
|
|
19
21
|
isVariant,
|
|
20
22
|
} from '../types';
|
|
21
23
|
import { assert } from '../assert/assert';
|
|
22
|
-
import { squareRootBN, standardizeBaseAssetAmount } from '..';
|
|
24
|
+
import { squareRootBN, clampBN, standardizeBaseAssetAmount } from '..';
|
|
23
25
|
|
|
24
26
|
import { OraclePriceData } from '../oracles/types';
|
|
25
27
|
import {
|
|
@@ -28,6 +30,8 @@ import {
|
|
|
28
30
|
calculateBudgetedPeg,
|
|
29
31
|
} from './repeg';
|
|
30
32
|
|
|
33
|
+
import { calculateLiveOracleStd } from './oracles';
|
|
34
|
+
|
|
31
35
|
export function calculatePegFromTargetPrice(
|
|
32
36
|
targetPrice: BN,
|
|
33
37
|
baseAssetReserve: BN,
|
|
@@ -63,7 +67,8 @@ export function calculateOptimalPegAndBudget(
|
|
|
63
67
|
const totalFeeLB = amm.totalExchangeFee.div(new BN(2));
|
|
64
68
|
const budget = BN.max(ZERO, amm.totalFeeMinusDistributions.sub(totalFeeLB));
|
|
65
69
|
if (budget.lt(prePegCost)) {
|
|
66
|
-
const
|
|
70
|
+
const halfMaxPriceSpread = new BN(amm.maxSpread)
|
|
71
|
+
.div(new BN(2))
|
|
67
72
|
.mul(targetPrice)
|
|
68
73
|
.div(BID_ASK_SPREAD_PRECISION);
|
|
69
74
|
|
|
@@ -72,8 +77,8 @@ export function calculateOptimalPegAndBudget(
|
|
|
72
77
|
let newBudget: BN;
|
|
73
78
|
const targetPriceGap = reservePriceBefore.sub(targetPrice);
|
|
74
79
|
|
|
75
|
-
if (targetPriceGap.abs().gt(
|
|
76
|
-
const markAdj = targetPriceGap.abs().sub(
|
|
80
|
+
if (targetPriceGap.abs().gt(halfMaxPriceSpread)) {
|
|
81
|
+
const markAdj = targetPriceGap.abs().sub(halfMaxPriceSpread);
|
|
77
82
|
|
|
78
83
|
if (targetPriceGap.lt(new BN(0))) {
|
|
79
84
|
newTargetPrice = reservePriceBefore.add(markAdj);
|
|
@@ -173,6 +178,8 @@ export function calculateUpdatedAMM(
|
|
|
173
178
|
|
|
174
179
|
newAmm.totalFeeMinusDistributions =
|
|
175
180
|
newAmm.totalFeeMinusDistributions.sub(prepegCost);
|
|
181
|
+
newAmm.netRevenueSinceLastFunding =
|
|
182
|
+
newAmm.netRevenueSinceLastFunding.sub(prepegCost);
|
|
176
183
|
|
|
177
184
|
return newAmm;
|
|
178
185
|
}
|
|
@@ -315,14 +322,14 @@ export function calculateMarketOpenBidAsk(
|
|
|
315
322
|
): [BN, BN] {
|
|
316
323
|
// open orders
|
|
317
324
|
let openAsks;
|
|
318
|
-
if (maxBaseAssetReserve
|
|
325
|
+
if (maxBaseAssetReserve.gt(baseAssetReserve)) {
|
|
319
326
|
openAsks = maxBaseAssetReserve.sub(baseAssetReserve).mul(new BN(-1));
|
|
320
327
|
} else {
|
|
321
328
|
openAsks = ZERO;
|
|
322
329
|
}
|
|
323
330
|
|
|
324
331
|
let openBids;
|
|
325
|
-
if (minBaseAssetReserve
|
|
332
|
+
if (minBaseAssetReserve.lt(baseAssetReserve)) {
|
|
326
333
|
openBids = baseAssetReserve.sub(minBaseAssetReserve);
|
|
327
334
|
} else {
|
|
328
335
|
openBids = ZERO;
|
|
@@ -331,12 +338,18 @@ export function calculateMarketOpenBidAsk(
|
|
|
331
338
|
}
|
|
332
339
|
|
|
333
340
|
export function calculateInventoryScale(
|
|
334
|
-
|
|
341
|
+
baseAssetAmountWithAmm: BN,
|
|
335
342
|
baseAssetReserve: BN,
|
|
336
343
|
minBaseAssetReserve: BN,
|
|
337
|
-
maxBaseAssetReserve: BN
|
|
344
|
+
maxBaseAssetReserve: BN,
|
|
345
|
+
directionalSpread: number,
|
|
346
|
+
maxSpread: number
|
|
338
347
|
): number {
|
|
339
|
-
|
|
348
|
+
if (baseAssetAmountWithAmm.eq(ZERO)) {
|
|
349
|
+
return 0;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
const defaultLargeBidAskFactor = BID_ASK_SPREAD_PRECISION.mul(new BN(10));
|
|
340
353
|
// inventory skew
|
|
341
354
|
const [openBids, openAsks] = calculateMarketOpenBidAsk(
|
|
342
355
|
baseAssetReserve,
|
|
@@ -348,13 +361,27 @@ export function calculateInventoryScale(
|
|
|
348
361
|
new BN(1),
|
|
349
362
|
BN.min(openBids.abs(), openAsks.abs())
|
|
350
363
|
);
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
364
|
+
|
|
365
|
+
const inventoryScaleMax =
|
|
366
|
+
BN.max(
|
|
367
|
+
defaultLargeBidAskFactor,
|
|
368
|
+
new BN(maxSpread / 2)
|
|
369
|
+
.mul(BID_ASK_SPREAD_PRECISION)
|
|
370
|
+
.div(new BN(Math.max(directionalSpread, 1)))
|
|
355
371
|
).toNumber() / BID_ASK_SPREAD_PRECISION.toNumber();
|
|
356
372
|
|
|
357
|
-
|
|
373
|
+
const inventoryScale =
|
|
374
|
+
baseAssetAmountWithAmm
|
|
375
|
+
.mul(BN.max(baseAssetAmountWithAmm.abs(), BASE_PRECISION))
|
|
376
|
+
.div(BASE_PRECISION)
|
|
377
|
+
.mul(defaultLargeBidAskFactor)
|
|
378
|
+
.div(minSideLiquidity)
|
|
379
|
+
.abs()
|
|
380
|
+
.toNumber() / BID_ASK_SPREAD_PRECISION.toNumber();
|
|
381
|
+
|
|
382
|
+
const inventorySpreadScale = Math.min(inventoryScaleMax, 1 + inventoryScale);
|
|
383
|
+
|
|
384
|
+
return inventorySpreadScale;
|
|
358
385
|
}
|
|
359
386
|
|
|
360
387
|
export function calculateEffectiveLeverage(
|
|
@@ -392,6 +419,47 @@ export function calculateMaxSpread(marginRatioInitial: number): number {
|
|
|
392
419
|
return maxTargetSpread;
|
|
393
420
|
}
|
|
394
421
|
|
|
422
|
+
export function calculateVolSpreadBN(
|
|
423
|
+
lastOracleConfPct: BN,
|
|
424
|
+
reservePrice: BN,
|
|
425
|
+
markStd: BN,
|
|
426
|
+
oracleStd: BN,
|
|
427
|
+
longIntensity: BN,
|
|
428
|
+
shortIntensity: BN,
|
|
429
|
+
volume24H: BN
|
|
430
|
+
): [BN, BN] {
|
|
431
|
+
const marketAvgStdPct = markStd
|
|
432
|
+
.add(oracleStd)
|
|
433
|
+
.mul(PERCENTAGE_PRECISION)
|
|
434
|
+
.div(reservePrice.mul(new BN(2)));
|
|
435
|
+
const volSpread = BN.max(lastOracleConfPct, marketAvgStdPct.div(new BN(2)));
|
|
436
|
+
|
|
437
|
+
const clampMax = PERCENTAGE_PRECISION.mul(new BN(16)).div(new BN(10));
|
|
438
|
+
const clampMin = PERCENTAGE_PRECISION.div(new BN(10));
|
|
439
|
+
|
|
440
|
+
const longVolSpreadFactor = clampBN(
|
|
441
|
+
longIntensity.mul(PERCENTAGE_PRECISION).div(BN.max(ONE, volume24H)),
|
|
442
|
+
clampMin,
|
|
443
|
+
clampMax
|
|
444
|
+
);
|
|
445
|
+
const shortVolSpreadFactor = clampBN(
|
|
446
|
+
shortIntensity.mul(PERCENTAGE_PRECISION).div(BN.max(ONE, volume24H)),
|
|
447
|
+
clampMin,
|
|
448
|
+
clampMax
|
|
449
|
+
);
|
|
450
|
+
|
|
451
|
+
const longVolSpread = BN.max(
|
|
452
|
+
lastOracleConfPct,
|
|
453
|
+
volSpread.mul(longVolSpreadFactor).div(PERCENTAGE_PRECISION)
|
|
454
|
+
);
|
|
455
|
+
const shortVolSpread = BN.max(
|
|
456
|
+
lastOracleConfPct,
|
|
457
|
+
volSpread.mul(shortVolSpreadFactor).div(PERCENTAGE_PRECISION)
|
|
458
|
+
);
|
|
459
|
+
|
|
460
|
+
return [longVolSpread, shortVolSpread];
|
|
461
|
+
}
|
|
462
|
+
|
|
395
463
|
export function calculateSpreadBN(
|
|
396
464
|
baseSpread: number,
|
|
397
465
|
lastOracleReservePriceSpreadPct: BN,
|
|
@@ -400,27 +468,42 @@ export function calculateSpreadBN(
|
|
|
400
468
|
quoteAssetReserve: BN,
|
|
401
469
|
terminalQuoteAssetReserve: BN,
|
|
402
470
|
pegMultiplier: BN,
|
|
403
|
-
|
|
471
|
+
baseAssetAmountWithAmm: BN,
|
|
404
472
|
reservePrice: BN,
|
|
405
473
|
totalFeeMinusDistributions: BN,
|
|
406
474
|
baseAssetReserve: BN,
|
|
407
475
|
minBaseAssetReserve: BN,
|
|
408
|
-
maxBaseAssetReserve: BN
|
|
476
|
+
maxBaseAssetReserve: BN,
|
|
477
|
+
markStd: BN,
|
|
478
|
+
oracleStd: BN,
|
|
479
|
+
longIntensity: BN,
|
|
480
|
+
shortIntensity: BN,
|
|
481
|
+
volume24H: BN
|
|
409
482
|
): [number, number] {
|
|
410
|
-
|
|
411
|
-
|
|
483
|
+
const [longVolSpread, shortVolSpread] = calculateVolSpreadBN(
|
|
484
|
+
lastOracleConfPct,
|
|
485
|
+
reservePrice,
|
|
486
|
+
markStd,
|
|
487
|
+
oracleStd,
|
|
488
|
+
longIntensity,
|
|
489
|
+
shortIntensity,
|
|
490
|
+
volume24H
|
|
491
|
+
);
|
|
492
|
+
|
|
493
|
+
let longSpread = Math.max(baseSpread / 2, longVolSpread.toNumber());
|
|
494
|
+
let shortSpread = Math.max(baseSpread / 2, shortVolSpread.toNumber());
|
|
412
495
|
|
|
413
|
-
if (lastOracleReservePriceSpreadPct.
|
|
496
|
+
if (lastOracleReservePriceSpreadPct.gt(ZERO)) {
|
|
414
497
|
shortSpread = Math.max(
|
|
415
498
|
shortSpread,
|
|
416
499
|
lastOracleReservePriceSpreadPct.abs().toNumber() +
|
|
417
|
-
|
|
500
|
+
shortVolSpread.toNumber()
|
|
418
501
|
);
|
|
419
502
|
} else if (lastOracleReservePriceSpreadPct.lt(ZERO)) {
|
|
420
503
|
longSpread = Math.max(
|
|
421
504
|
longSpread,
|
|
422
505
|
lastOracleReservePriceSpreadPct.abs().toNumber() +
|
|
423
|
-
|
|
506
|
+
longVolSpread.toNumber()
|
|
424
507
|
);
|
|
425
508
|
}
|
|
426
509
|
|
|
@@ -429,22 +512,18 @@ export function calculateSpreadBN(
|
|
|
429
512
|
lastOracleReservePriceSpreadPct.abs().toNumber()
|
|
430
513
|
);
|
|
431
514
|
|
|
432
|
-
const
|
|
433
|
-
|
|
434
|
-
const inventoryScale = calculateInventoryScale(
|
|
435
|
-
netBaseAssetAmount,
|
|
515
|
+
const inventorySpreadScale = calculateInventoryScale(
|
|
516
|
+
baseAssetAmountWithAmm,
|
|
436
517
|
baseAssetReserve,
|
|
437
518
|
minBaseAssetReserve,
|
|
438
|
-
maxBaseAssetReserve
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
MAX_BID_ASK_INVENTORY_SKEW_FACTOR,
|
|
442
|
-
1 + inventoryScale
|
|
519
|
+
maxBaseAssetReserve,
|
|
520
|
+
baseAssetAmountWithAmm.gt(ZERO) ? longSpread : shortSpread,
|
|
521
|
+
maxTargetSpread
|
|
443
522
|
);
|
|
444
523
|
|
|
445
|
-
if (
|
|
524
|
+
if (baseAssetAmountWithAmm.gt(ZERO)) {
|
|
446
525
|
longSpread *= inventorySpreadScale;
|
|
447
|
-
} else if (
|
|
526
|
+
} else if (baseAssetAmountWithAmm.lt(ZERO)) {
|
|
448
527
|
shortSpread *= inventorySpreadScale;
|
|
449
528
|
}
|
|
450
529
|
|
|
@@ -453,24 +532,22 @@ export function calculateSpreadBN(
|
|
|
453
532
|
quoteAssetReserve,
|
|
454
533
|
terminalQuoteAssetReserve,
|
|
455
534
|
pegMultiplier,
|
|
456
|
-
|
|
535
|
+
baseAssetAmountWithAmm,
|
|
457
536
|
reservePrice,
|
|
458
537
|
totalFeeMinusDistributions
|
|
459
538
|
);
|
|
460
539
|
|
|
540
|
+
const MAX_SPREAD_SCALE = 10;
|
|
461
541
|
if (totalFeeMinusDistributions.gt(ZERO)) {
|
|
462
|
-
const spreadScale = Math.min(
|
|
463
|
-
|
|
464
|
-
1 + effectiveLeverage
|
|
465
|
-
);
|
|
466
|
-
if (netBaseAssetAmount.gt(ZERO)) {
|
|
542
|
+
const spreadScale = Math.min(MAX_SPREAD_SCALE, 1 + effectiveLeverage);
|
|
543
|
+
if (baseAssetAmountWithAmm.gt(ZERO)) {
|
|
467
544
|
longSpread *= spreadScale;
|
|
468
545
|
} else {
|
|
469
546
|
shortSpread *= spreadScale;
|
|
470
547
|
}
|
|
471
548
|
} else {
|
|
472
|
-
longSpread *=
|
|
473
|
-
shortSpread *=
|
|
549
|
+
longSpread *= MAX_SPREAD_SCALE;
|
|
550
|
+
shortSpread *= MAX_SPREAD_SCALE;
|
|
474
551
|
}
|
|
475
552
|
|
|
476
553
|
const totalSpread = longSpread + shortSpread;
|
|
@@ -504,7 +581,6 @@ export function calculateSpread(
|
|
|
504
581
|
|
|
505
582
|
const targetPrice = oraclePriceData?.price || reservePrice;
|
|
506
583
|
const confInterval = oraclePriceData.confidence || ZERO;
|
|
507
|
-
|
|
508
584
|
const targetMarkSpreadPct = reservePrice
|
|
509
585
|
.sub(targetPrice)
|
|
510
586
|
.mul(BID_ASK_SPREAD_PRECISION)
|
|
@@ -514,6 +590,9 @@ export function calculateSpread(
|
|
|
514
590
|
.mul(BID_ASK_SPREAD_PRECISION)
|
|
515
591
|
.div(reservePrice);
|
|
516
592
|
|
|
593
|
+
const now = new BN(new Date().getTime() / 1000); //todo
|
|
594
|
+
const liveOracleStd = calculateLiveOracleStd(amm, oraclePriceData, now);
|
|
595
|
+
|
|
517
596
|
const [longSpread, shortSpread] = calculateSpreadBN(
|
|
518
597
|
amm.baseSpread,
|
|
519
598
|
targetMarkSpreadPct,
|
|
@@ -527,7 +606,12 @@ export function calculateSpread(
|
|
|
527
606
|
amm.totalFeeMinusDistributions,
|
|
528
607
|
amm.baseAssetReserve,
|
|
529
608
|
amm.minBaseAssetReserve,
|
|
530
|
-
amm.maxBaseAssetReserve
|
|
609
|
+
amm.maxBaseAssetReserve,
|
|
610
|
+
amm.markStd,
|
|
611
|
+
liveOracleStd,
|
|
612
|
+
amm.longIntensityVolume,
|
|
613
|
+
amm.shortIntensityVolume,
|
|
614
|
+
amm.volume24H
|
|
531
615
|
);
|
|
532
616
|
|
|
533
617
|
let spread: number;
|
package/src/math/oracles.ts
CHANGED
|
@@ -88,3 +88,55 @@ export function isOracleTooDivergent(
|
|
|
88
88
|
|
|
89
89
|
return tooDivergent;
|
|
90
90
|
}
|
|
91
|
+
|
|
92
|
+
export function calculateLiveOracleTwap(
|
|
93
|
+
amm: AMM,
|
|
94
|
+
oraclePriceData: OraclePriceData,
|
|
95
|
+
now: BN
|
|
96
|
+
): BN {
|
|
97
|
+
const sinceLastUpdate = now.sub(
|
|
98
|
+
amm.historicalOracleData.lastOraclePriceTwapTs
|
|
99
|
+
);
|
|
100
|
+
const sinceStart = BN.max(ZERO, amm.fundingPeriod.sub(sinceLastUpdate));
|
|
101
|
+
|
|
102
|
+
const clampRange = amm.historicalOracleData.lastOraclePriceTwap.div(
|
|
103
|
+
new BN(3)
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
const clampedOraclePrice = BN.min(
|
|
107
|
+
amm.historicalOracleData.lastOraclePriceTwap.add(clampRange),
|
|
108
|
+
BN.max(
|
|
109
|
+
oraclePriceData.price,
|
|
110
|
+
amm.historicalOracleData.lastOraclePriceTwap.sub(clampRange)
|
|
111
|
+
)
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
const newOracleTwap = amm.historicalOracleData.lastOraclePriceTwap
|
|
115
|
+
.mul(sinceStart)
|
|
116
|
+
.add(clampedOraclePrice)
|
|
117
|
+
.mul(sinceLastUpdate)
|
|
118
|
+
.div(sinceStart.add(sinceLastUpdate));
|
|
119
|
+
|
|
120
|
+
return newOracleTwap;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export function calculateLiveOracleStd(
|
|
124
|
+
amm: AMM,
|
|
125
|
+
oraclePriceData: OraclePriceData,
|
|
126
|
+
now: BN
|
|
127
|
+
): BN {
|
|
128
|
+
const sinceLastUpdate = now.sub(
|
|
129
|
+
amm.historicalOracleData.lastOraclePriceTwapTs
|
|
130
|
+
);
|
|
131
|
+
const sinceStart = BN.max(ZERO, amm.fundingPeriod.sub(sinceLastUpdate));
|
|
132
|
+
|
|
133
|
+
const liveOracleTwap = calculateLiveOracleTwap(amm, oraclePriceData, now);
|
|
134
|
+
|
|
135
|
+
const priceDeltaVsTwap = oraclePriceData.price.sub(liveOracleTwap).abs();
|
|
136
|
+
|
|
137
|
+
const oracleStd = priceDeltaVsTwap.add(
|
|
138
|
+
amm.oracleStd.mul(sinceStart).div(sinceStart.add(sinceLastUpdate))
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
return oracleStd;
|
|
142
|
+
}
|
package/src/math/orders.ts
CHANGED
|
@@ -127,8 +127,9 @@ export function standardizeBaseAssetAmount(
|
|
|
127
127
|
export function getLimitPrice(
|
|
128
128
|
order: Order,
|
|
129
129
|
oraclePriceData: OraclePriceData,
|
|
130
|
-
slot: number
|
|
131
|
-
|
|
130
|
+
slot: number,
|
|
131
|
+
fallbackPrice?: BN
|
|
132
|
+
): BN | undefined {
|
|
132
133
|
let limitPrice;
|
|
133
134
|
if (order.oraclePriceOffset !== 0) {
|
|
134
135
|
limitPrice = oraclePriceData.price.add(new BN(order.oraclePriceOffset));
|
|
@@ -138,13 +139,7 @@ export function getLimitPrice(
|
|
|
138
139
|
} else if (!order.price.eq(ZERO)) {
|
|
139
140
|
limitPrice = order.price;
|
|
140
141
|
} else {
|
|
141
|
-
|
|
142
|
-
const oraclePrice1Pct = oraclePriceData.price.div(new BN(100));
|
|
143
|
-
if (isVariant(order.direction, 'long')) {
|
|
144
|
-
limitPrice = oraclePriceData.price.add(oraclePrice1Pct);
|
|
145
|
-
} else {
|
|
146
|
-
limitPrice = oraclePriceData.price.sub(oraclePrice1Pct);
|
|
147
|
-
}
|
|
142
|
+
limitPrice = fallbackPrice;
|
|
148
143
|
}
|
|
149
144
|
} else {
|
|
150
145
|
limitPrice = order.price;
|
|
@@ -153,18 +148,6 @@ export function getLimitPrice(
|
|
|
153
148
|
return limitPrice;
|
|
154
149
|
}
|
|
155
150
|
|
|
156
|
-
export function getOptionalLimitPrice(
|
|
157
|
-
order: Order,
|
|
158
|
-
oraclePriceData: OraclePriceData,
|
|
159
|
-
slot: number
|
|
160
|
-
): BN | undefined {
|
|
161
|
-
if (hasLimitPrice(order, slot)) {
|
|
162
|
-
return getLimitPrice(order, oraclePriceData, slot);
|
|
163
|
-
} else {
|
|
164
|
-
return undefined;
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
|
|
168
151
|
export function hasLimitPrice(order: Order, slot: number): boolean {
|
|
169
152
|
return (
|
|
170
153
|
order.price.gt(ZERO) ||
|
|
@@ -202,7 +185,7 @@ export function calculateBaseAssetAmountForAmmToFulfill(
|
|
|
202
185
|
return ZERO;
|
|
203
186
|
}
|
|
204
187
|
|
|
205
|
-
const limitPrice =
|
|
188
|
+
const limitPrice = getLimitPrice(order, oraclePriceData, slot);
|
|
206
189
|
let baseAssetAmount;
|
|
207
190
|
|
|
208
191
|
const updatedAMM = calculateUpdatedAMM(market.amm, oraclePriceData);
|
package/src/math/utils.ts
CHANGED
package/src/types.ts
CHANGED
|
@@ -519,6 +519,8 @@ export type StateAccount = {
|
|
|
519
519
|
perpFeeStructure: FeeStructure;
|
|
520
520
|
spotFeeStructure: FeeStructure;
|
|
521
521
|
lpCooldownTime: BN;
|
|
522
|
+
initialPctToLiquidate: number;
|
|
523
|
+
liquidationDuration: number;
|
|
522
524
|
};
|
|
523
525
|
|
|
524
526
|
export type PerpMarketAccount = {
|
|
@@ -745,8 +747,8 @@ export type PerpPosition = {
|
|
|
745
747
|
settledPnl: BN;
|
|
746
748
|
lpShares: BN;
|
|
747
749
|
remainderBaseAssetAmount: number;
|
|
748
|
-
|
|
749
|
-
|
|
750
|
+
lastBaseAssetAmountPerLp: BN;
|
|
751
|
+
lastQuoteAssetAmountPerLp: BN;
|
|
750
752
|
};
|
|
751
753
|
|
|
752
754
|
export type UserStatsAccount = {
|
|
@@ -790,6 +792,8 @@ export type UserAccount = {
|
|
|
790
792
|
totalWithdraws: BN;
|
|
791
793
|
totalSocialLoss: BN;
|
|
792
794
|
cumulativePerpFunding: BN;
|
|
795
|
+
liquidationMarginFreed: BN;
|
|
796
|
+
liquidationStartSlot: BN;
|
|
793
797
|
};
|
|
794
798
|
|
|
795
799
|
export type SpotPosition = {
|