@drift-labs/sdk 0.1.11 → 0.1.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 +2 -2
- package/lib/accounts/defaultClearingHouseAccountSubscriber.d.ts +1 -0
- package/lib/accounts/defaultClearingHouseAccountSubscriber.d.ts.map +1 -1
- package/lib/accounts/defaultClearingHouseAccountSubscriber.js +17 -0
- package/lib/accounts/defaultUserAccountSubscriber.d.ts +2 -0
- package/lib/accounts/defaultUserAccountSubscriber.d.ts.map +1 -1
- package/lib/accounts/defaultUserAccountSubscriber.js +16 -0
- package/lib/accounts/types.d.ts +3 -21
- package/lib/accounts/types.d.ts.map +1 -1
- package/lib/accounts/webSocketAccountSubscriber.d.ts +2 -0
- package/lib/accounts/webSocketAccountSubscriber.d.ts.map +1 -1
- package/lib/accounts/webSocketAccountSubscriber.js +13 -3
- package/lib/clearingHouse.d.ts +4 -0
- package/lib/clearingHouse.d.ts.map +1 -1
- package/lib/clearingHouse.js +8 -0
- package/lib/clearingHouseUser.d.ts +33 -5
- package/lib/clearingHouseUser.d.ts.map +1 -1
- package/lib/clearingHouseUser.js +224 -78
- package/lib/constants/markets.d.ts.map +1 -1
- package/lib/constants/markets.js +21 -0
- package/lib/constants/numericConstants.d.ts +1 -0
- package/lib/constants/numericConstants.d.ts.map +1 -1
- package/lib/constants/numericConstants.js +2 -1
- package/lib/examples/makeTradeExample.d.ts.map +1 -1
- package/lib/examples/makeTradeExample.js +14 -13
- package/lib/idl/clearing_house.json +94 -42
- package/lib/index.d.ts +4 -1
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +4 -1
- package/lib/math/amm.d.ts +26 -1
- package/lib/math/amm.d.ts.map +1 -1
- package/lib/math/amm.js +85 -10
- package/lib/math/funding.d.ts +6 -6
- package/lib/math/funding.d.ts.map +1 -1
- package/lib/math/funding.js +69 -25
- package/lib/math/insuranceFund.d.ts +15 -0
- package/lib/math/insuranceFund.d.ts.map +1 -0
- package/lib/math/insuranceFund.js +36 -0
- package/lib/math/position.d.ts +1 -1
- package/lib/math/position.d.ts.map +1 -1
- package/lib/math/position.js +15 -23
- package/lib/math/trade.d.ts +1 -1
- package/lib/math/trade.d.ts.map +1 -1
- package/lib/math/trade.js +9 -4
- package/lib/types.d.ts +0 -50
- package/lib/types.d.ts.map +1 -1
- package/lib/util/computeUnits.d.ts +3 -0
- package/lib/util/computeUnits.d.ts.map +1 -0
- package/lib/util/computeUnits.js +27 -0
- package/lib/util/tps.d.ts +3 -0
- package/lib/util/tps.d.ts.map +1 -0
- package/lib/util/tps.js +27 -0
- package/lib/wallet.d.ts +10 -0
- package/lib/wallet.d.ts.map +1 -0
- package/lib/wallet.js +35 -0
- package/package.json +3 -13
- package/src/accounts/defaultClearingHouseAccountSubscriber.ts +18 -0
- package/src/accounts/defaultUserAccountSubscriber.ts +18 -0
- package/src/accounts/types.ts +3 -28
- package/src/accounts/webSocketAccountSubscriber.ts +16 -6
- package/src/clearingHouse.ts +9 -3
- package/src/clearingHouseUser.ts +325 -107
- package/src/constants/markets.ts +21 -0
- package/src/constants/numericConstants.ts +2 -0
- package/src/examples/makeTradeExample.ts +2 -1
- package/src/idl/clearing_house.json +94 -42
- package/src/index.ts +4 -1
- package/src/math/amm.ts +120 -13
- package/src/math/funding.ts +109 -51
- package/src/math/insuranceFund.ts +29 -0
- package/src/math/position.ts +16 -28
- package/src/math/trade.ts +9 -5
- package/src/types.ts +0 -54
- package/src/util/computeUnits.ts +21 -0
- package/src/util/tps.ts +27 -0
- package/src/wallet.ts +22 -0
- package/.eslintrc.json +0 -36
- package/.prettierignore +0 -1
- package/.prettierrc.js +0 -9
- package/lib/accounts/defaultHistoryAccountSubscriber.d.ts +0 -29
- package/lib/accounts/defaultHistoryAccountSubscriber.d.ts.map +0 -1
- package/lib/accounts/defaultHistoryAccountSubscriber.js +0 -110
- package/src/accounts/defaultHistoryAccountSubscriber.ts +0 -179
package/src/clearingHouseUser.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { EventEmitter } from 'events';
|
|
|
4
4
|
import StrictEventEmitter from 'strict-event-emitter-types';
|
|
5
5
|
import { ClearingHouse } from './clearingHouse';
|
|
6
6
|
import { UserAccount, UserPosition, UserPositionsAccount } from './types';
|
|
7
|
+
import { calculateEntryPrice } from './math/position';
|
|
7
8
|
import {
|
|
8
9
|
MARK_PRICE_PRECISION,
|
|
9
10
|
AMM_TO_QUOTE_PRECISION_RATIO,
|
|
@@ -13,6 +14,8 @@ import {
|
|
|
13
14
|
PARTIAL_LIQUIDATION_RATIO,
|
|
14
15
|
FULL_LIQUIDATION_RATIO,
|
|
15
16
|
QUOTE_PRECISION,
|
|
17
|
+
AMM_RESERVE_PRECISION,
|
|
18
|
+
PRICE_TO_QUOTE_PRECISION,
|
|
16
19
|
} from './constants/numericConstants';
|
|
17
20
|
import { UserAccountSubscriber, UserAccountEvents } from './accounts/types';
|
|
18
21
|
import { DefaultUserAccountSubscriber } from './accounts/defaultUserAccountSubscriber';
|
|
@@ -22,6 +25,7 @@ import {
|
|
|
22
25
|
calculatePositionFundingPNL,
|
|
23
26
|
calculatePositionPNL,
|
|
24
27
|
PositionDirection,
|
|
28
|
+
calculateTradeSlippage,
|
|
25
29
|
} from '.';
|
|
26
30
|
import { getUserAccountPublicKey } from './addresses';
|
|
27
31
|
|
|
@@ -67,6 +71,13 @@ export class ClearingHouseUser {
|
|
|
67
71
|
return this.isSubscribed;
|
|
68
72
|
}
|
|
69
73
|
|
|
74
|
+
/**
|
|
75
|
+
* Forces the accountSubscriber to fetch account updates from rpc
|
|
76
|
+
*/
|
|
77
|
+
public async fetchAccounts(): Promise<void> {
|
|
78
|
+
await this.accountSubscriber.fetch();
|
|
79
|
+
}
|
|
80
|
+
|
|
70
81
|
public async unsubscribe(): Promise<void> {
|
|
71
82
|
await this.accountSubscriber.unsubscribe();
|
|
72
83
|
this.isSubscribed = false;
|
|
@@ -81,23 +92,25 @@ export class ClearingHouseUser {
|
|
|
81
92
|
}
|
|
82
93
|
|
|
83
94
|
/**
|
|
84
|
-
* Gets the user's current position for a given market
|
|
95
|
+
* Gets the user's current position for a given market. If the user has no position returns undefined
|
|
85
96
|
* @param marketIndex
|
|
86
97
|
* @returns userPosition
|
|
87
98
|
*/
|
|
88
|
-
public getUserPosition(marketIndex: BN): UserPosition {
|
|
89
|
-
return (
|
|
90
|
-
|
|
91
|
-
position.marketIndex.eq(marketIndex)
|
|
92
|
-
) ?? {
|
|
93
|
-
baseAssetAmount: ZERO,
|
|
94
|
-
lastCumulativeFundingRate: ZERO,
|
|
95
|
-
marketIndex,
|
|
96
|
-
quoteAssetAmount: ZERO,
|
|
97
|
-
}
|
|
99
|
+
public getUserPosition(marketIndex: BN): UserPosition | undefined {
|
|
100
|
+
return this.getUserPositionsAccount().positions.find((position) =>
|
|
101
|
+
position.marketIndex.eq(marketIndex)
|
|
98
102
|
);
|
|
99
103
|
}
|
|
100
104
|
|
|
105
|
+
public getEmptyPosition(marketIndex: BN): UserPosition {
|
|
106
|
+
return {
|
|
107
|
+
baseAssetAmount: ZERO,
|
|
108
|
+
lastCumulativeFundingRate: ZERO,
|
|
109
|
+
marketIndex,
|
|
110
|
+
quoteAssetAmount: ZERO,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
|
|
101
114
|
public async getUserAccountPublicKey(): Promise<PublicKey> {
|
|
102
115
|
if (this.userAccountPublicKey) {
|
|
103
116
|
return this.userAccountPublicKey;
|
|
@@ -145,30 +158,32 @@ export class ClearingHouseUser {
|
|
|
145
158
|
* calculates unrealized position price pnl
|
|
146
159
|
* @returns : Precision QUOTE_PRECISION
|
|
147
160
|
*/
|
|
148
|
-
public getUnrealizedPNL(withFunding?: boolean): BN {
|
|
149
|
-
return this.getUserPositionsAccount()
|
|
150
|
-
(
|
|
161
|
+
public getUnrealizedPNL(withFunding?: boolean, marketIndex?: BN): BN {
|
|
162
|
+
return this.getUserPositionsAccount()
|
|
163
|
+
.positions.filter((pos) =>
|
|
164
|
+
marketIndex ? pos.marketIndex === marketIndex : true
|
|
165
|
+
)
|
|
166
|
+
.reduce((pnl, marketPosition) => {
|
|
151
167
|
const market = this.clearingHouse.getMarket(marketPosition.marketIndex);
|
|
152
168
|
return pnl.add(
|
|
153
169
|
calculatePositionPNL(market, marketPosition, withFunding)
|
|
154
170
|
);
|
|
155
|
-
},
|
|
156
|
-
ZERO
|
|
157
|
-
);
|
|
171
|
+
}, ZERO);
|
|
158
172
|
}
|
|
159
173
|
|
|
160
174
|
/**
|
|
161
175
|
* calculates unrealized funding payment pnl
|
|
162
176
|
* @returns : Precision QUOTE_PRECISION
|
|
163
177
|
*/
|
|
164
|
-
public getUnrealizedFundingPNL(): BN {
|
|
165
|
-
return this.getUserPositionsAccount()
|
|
166
|
-
(
|
|
178
|
+
public getUnrealizedFundingPNL(marketIndex?: BN): BN {
|
|
179
|
+
return this.getUserPositionsAccount()
|
|
180
|
+
.positions.filter((pos) =>
|
|
181
|
+
marketIndex ? pos.marketIndex === marketIndex : true
|
|
182
|
+
)
|
|
183
|
+
.reduce((pnl, marketPosition) => {
|
|
167
184
|
const market = this.clearingHouse.getMarket(marketPosition.marketIndex);
|
|
168
185
|
return pnl.add(calculatePositionFundingPNL(market, marketPosition));
|
|
169
|
-
},
|
|
170
|
-
ZERO
|
|
171
|
-
);
|
|
186
|
+
}, ZERO);
|
|
172
187
|
}
|
|
173
188
|
|
|
174
189
|
/**
|
|
@@ -203,7 +218,8 @@ export class ClearingHouseUser {
|
|
|
203
218
|
* @returns : Precision QUOTE_PRECISION
|
|
204
219
|
*/
|
|
205
220
|
public getPositionValue(marketIndex: BN): BN {
|
|
206
|
-
const userPosition =
|
|
221
|
+
const userPosition =
|
|
222
|
+
this.getUserPosition(marketIndex) || this.getEmptyPosition(marketIndex);
|
|
207
223
|
const market = this.clearingHouse.getMarket(userPosition.marketIndex);
|
|
208
224
|
return calculateBaseAssetValue(market, userPosition);
|
|
209
225
|
}
|
|
@@ -224,15 +240,17 @@ export class ClearingHouseUser {
|
|
|
224
240
|
* calculates average exit price for closing 100% of position
|
|
225
241
|
* @returns : Precision MARK_PRICE_PRECISION
|
|
226
242
|
*/
|
|
227
|
-
public
|
|
243
|
+
public getPositionEstimatedExitPriceAndPnl(
|
|
228
244
|
position: UserPosition,
|
|
229
245
|
amountToClose?: BN
|
|
230
|
-
): BN {
|
|
246
|
+
): [BN, BN] {
|
|
231
247
|
const market = this.clearingHouse.getMarket(position.marketIndex);
|
|
232
248
|
|
|
249
|
+
const entryPrice = calculateEntryPrice(position);
|
|
250
|
+
|
|
233
251
|
if (amountToClose) {
|
|
234
252
|
if (amountToClose.eq(ZERO)) {
|
|
235
|
-
return calculateMarkPrice(market);
|
|
253
|
+
return [calculateMarkPrice(market), ZERO];
|
|
236
254
|
}
|
|
237
255
|
position = {
|
|
238
256
|
baseAssetAmount: amountToClose,
|
|
@@ -244,12 +262,21 @@ export class ClearingHouseUser {
|
|
|
244
262
|
|
|
245
263
|
const baseAssetValue = calculateBaseAssetValue(market, position);
|
|
246
264
|
if (position.baseAssetAmount.eq(ZERO)) {
|
|
247
|
-
return ZERO;
|
|
265
|
+
return [ZERO, ZERO];
|
|
248
266
|
}
|
|
249
|
-
|
|
267
|
+
|
|
268
|
+
const exitPrice = baseAssetValue
|
|
250
269
|
.mul(AMM_TO_QUOTE_PRECISION_RATIO)
|
|
251
270
|
.mul(MARK_PRICE_PRECISION)
|
|
252
271
|
.div(position.baseAssetAmount.abs());
|
|
272
|
+
|
|
273
|
+
const pnlPerBase = exitPrice.sub(entryPrice);
|
|
274
|
+
const pnl = pnlPerBase
|
|
275
|
+
.mul(position.baseAssetAmount)
|
|
276
|
+
.div(MARK_PRICE_PRECISION)
|
|
277
|
+
.div(AMM_TO_QUOTE_PRECISION_RATIO);
|
|
278
|
+
|
|
279
|
+
return [exitPrice, pnl];
|
|
253
280
|
}
|
|
254
281
|
|
|
255
282
|
/**
|
|
@@ -348,7 +375,7 @@ export class ClearingHouseUser {
|
|
|
348
375
|
* @param partial
|
|
349
376
|
* @returns Precision : MARK_PRICE_PRECISION
|
|
350
377
|
*/
|
|
351
|
-
public
|
|
378
|
+
public liquidationPriceOld(
|
|
352
379
|
targetMarket: Pick<UserPosition, 'marketIndex'>,
|
|
353
380
|
positionBaseSizeChange: BN = ZERO,
|
|
354
381
|
partial = false
|
|
@@ -377,13 +404,11 @@ export class ClearingHouseUser {
|
|
|
377
404
|
const totalCurrentPositionValueIgnoringTargetUSDC =
|
|
378
405
|
this.getTotalPositionValueExcludingMarket(targetMarket.marketIndex);
|
|
379
406
|
|
|
380
|
-
const currentMarketPosition =
|
|
381
|
-
targetMarket.marketIndex
|
|
382
|
-
|
|
407
|
+
const currentMarketPosition =
|
|
408
|
+
this.getUserPosition(targetMarket.marketIndex) ||
|
|
409
|
+
this.getEmptyPosition(targetMarket.marketIndex);
|
|
383
410
|
|
|
384
|
-
const currentMarketPositionBaseSize = currentMarketPosition
|
|
385
|
-
? currentMarketPosition.baseAssetAmount
|
|
386
|
-
: ZERO;
|
|
411
|
+
const currentMarketPositionBaseSize = currentMarketPosition.baseAssetAmount;
|
|
387
412
|
|
|
388
413
|
// calculate position for current market after trade
|
|
389
414
|
const proposedMarketPosition: UserPosition = {
|
|
@@ -410,24 +435,36 @@ export class ClearingHouseUser {
|
|
|
410
435
|
proposedMarketPositionValueUSDC
|
|
411
436
|
);
|
|
412
437
|
|
|
438
|
+
let totalFreeCollateralUSDC = this.getTotalCollateral().sub(
|
|
439
|
+
this.getTotalPositionValue()
|
|
440
|
+
.mul(TEN_THOUSAND)
|
|
441
|
+
.div(this.getMaxLeverage('Maintenance'))
|
|
442
|
+
);
|
|
443
|
+
|
|
444
|
+
if (partial) {
|
|
445
|
+
totalFreeCollateralUSDC = this.getTotalCollateral().sub(
|
|
446
|
+
this.getTotalPositionValue()
|
|
447
|
+
.mul(TEN_THOUSAND)
|
|
448
|
+
.div(this.getMaxLeverage('Partial'))
|
|
449
|
+
);
|
|
450
|
+
}
|
|
451
|
+
|
|
413
452
|
// if the position value after the trade is less than total collateral, there is no liq price
|
|
414
|
-
if (
|
|
453
|
+
if (
|
|
454
|
+
targetTotalPositionValueUSDC.lte(totalFreeCollateralUSDC) &&
|
|
455
|
+
proposedMarketPosition.baseAssetAmount.gt(ZERO)
|
|
456
|
+
) {
|
|
415
457
|
return new BN(-1);
|
|
416
458
|
}
|
|
417
459
|
|
|
418
|
-
// proportion of proposed market position to overall position
|
|
419
|
-
// const marketProportion = proposedMarketPositionValueUSDC
|
|
420
|
-
// .mul(TEN_THOUSAND)
|
|
421
|
-
// .div(targetTotalPositionValueUSDC);
|
|
422
|
-
|
|
423
460
|
// get current margin ratio based on current collateral and proposed total position value
|
|
424
461
|
let marginRatio;
|
|
425
|
-
if (
|
|
462
|
+
if (proposedMarketPositionValueUSDC.eq(ZERO)) {
|
|
426
463
|
marginRatio = BN_MAX;
|
|
427
464
|
} else {
|
|
428
465
|
marginRatio = totalCollateralUSDC
|
|
429
466
|
.mul(TEN_THOUSAND)
|
|
430
|
-
.div(
|
|
467
|
+
.div(proposedMarketPositionValueUSDC);
|
|
431
468
|
}
|
|
432
469
|
|
|
433
470
|
let liqRatio = FULL_LIQUIDATION_RATIO;
|
|
@@ -438,29 +475,6 @@ export class ClearingHouseUser {
|
|
|
438
475
|
// sign of position in current market after the trade
|
|
439
476
|
const baseAssetSignIsNeg = proposedMarketPosition.baseAssetAmount.isNeg();
|
|
440
477
|
|
|
441
|
-
// console.log(
|
|
442
|
-
// convertToNumber(currentPrice),
|
|
443
|
-
// convertToNumber(liqRatio),
|
|
444
|
-
// convertToNumber(marginRatio),
|
|
445
|
-
// convertToNumber(marketProportion),
|
|
446
|
-
// );
|
|
447
|
-
|
|
448
|
-
// // if the user is long, then the liq price is the currentPrice multiplied by liqRatio/marginRatio (how many multiples lower does the current marginRatio have to go to reach the liqRatio), multiplied by the fraction of the proposed total position value that this market will take up
|
|
449
|
-
// if (!baseAssetSignIsNeg) {
|
|
450
|
-
// liqPrice = currentPrice
|
|
451
|
-
// .mul(liqRatio)
|
|
452
|
-
// .div(marginRatio)
|
|
453
|
-
// .mul(marketProportion)
|
|
454
|
-
// .div(TEN_THOUSAND);
|
|
455
|
-
// } else {
|
|
456
|
-
// // if the user is short, it's the reciprocal of the above
|
|
457
|
-
// liqPrice = currentPrice
|
|
458
|
-
// .mul(marginRatio)
|
|
459
|
-
// .div(liqRatio)
|
|
460
|
-
// .mul(TEN_THOUSAND)
|
|
461
|
-
// .div(marketProportion);
|
|
462
|
-
// }
|
|
463
|
-
|
|
464
478
|
let pctChange = marginRatio.abs().sub(liqRatio);
|
|
465
479
|
// if user is short, higher price is liq
|
|
466
480
|
if (baseAssetSignIsNeg) {
|
|
@@ -479,6 +493,143 @@ export class ClearingHouseUser {
|
|
|
479
493
|
return liqPrice;
|
|
480
494
|
}
|
|
481
495
|
|
|
496
|
+
/**
|
|
497
|
+
* Calculate the liquidation price of a position, with optional parameter to calculate the liquidation price after a trade
|
|
498
|
+
* @param targetMarket
|
|
499
|
+
* @param positionBaseSizeChange // change in position size to calculate liquidation price for : Precision 10^13
|
|
500
|
+
* @param partial
|
|
501
|
+
* @returns Precision : MARK_PRICE_PRECISION
|
|
502
|
+
*/
|
|
503
|
+
public liquidationPrice(
|
|
504
|
+
targetMarket: Pick<UserPosition, 'marketIndex'>,
|
|
505
|
+
positionBaseSizeChange: BN = ZERO,
|
|
506
|
+
partial = false
|
|
507
|
+
): BN {
|
|
508
|
+
// solves formula for example calc below
|
|
509
|
+
|
|
510
|
+
/* example: assume BTC price is $40k (examine 10% up/down)
|
|
511
|
+
|
|
512
|
+
if 10k deposit and levered 10x short BTC => BTC up $400 means:
|
|
513
|
+
1. higher base_asset_value (+$4k)
|
|
514
|
+
2. lower collateral (-$4k)
|
|
515
|
+
3. (10k - 4k)/(100k + 4k) => 6k/104k => .0576
|
|
516
|
+
|
|
517
|
+
for 10x long, BTC down $400:
|
|
518
|
+
3. (10k - 4k) / (100k - 4k) = 6k/96k => .0625 */
|
|
519
|
+
|
|
520
|
+
const tc = this.getTotalCollateral();
|
|
521
|
+
const tpv = this.getTotalPositionValue();
|
|
522
|
+
|
|
523
|
+
const partialLev = 16;
|
|
524
|
+
const maintLev = 20;
|
|
525
|
+
|
|
526
|
+
const thisLev = partial ? new BN(partialLev) : new BN(maintLev);
|
|
527
|
+
|
|
528
|
+
// calculate the total position value ignoring any value from the target market of the trade
|
|
529
|
+
const totalCurrentPositionValueIgnoringTargetUSDC =
|
|
530
|
+
this.getTotalPositionValueExcludingMarket(targetMarket.marketIndex);
|
|
531
|
+
|
|
532
|
+
const currentMarketPosition =
|
|
533
|
+
this.getUserPosition(targetMarket.marketIndex) ||
|
|
534
|
+
this.getEmptyPosition(targetMarket.marketIndex);
|
|
535
|
+
|
|
536
|
+
const currentMarketPositionBaseSize = currentMarketPosition.baseAssetAmount;
|
|
537
|
+
|
|
538
|
+
const proposedBaseAssetAmount = currentMarketPositionBaseSize.add(
|
|
539
|
+
positionBaseSizeChange
|
|
540
|
+
);
|
|
541
|
+
|
|
542
|
+
// calculate position for current market after trade
|
|
543
|
+
const proposedMarketPosition: UserPosition = {
|
|
544
|
+
marketIndex: targetMarket.marketIndex,
|
|
545
|
+
baseAssetAmount: proposedBaseAssetAmount,
|
|
546
|
+
lastCumulativeFundingRate:
|
|
547
|
+
currentMarketPosition.lastCumulativeFundingRate,
|
|
548
|
+
quoteAssetAmount: new BN(0),
|
|
549
|
+
};
|
|
550
|
+
|
|
551
|
+
const market = this.clearingHouse.getMarket(
|
|
552
|
+
proposedMarketPosition.marketIndex
|
|
553
|
+
);
|
|
554
|
+
|
|
555
|
+
const proposedMarketPositionValueUSDC = calculateBaseAssetValue(
|
|
556
|
+
market,
|
|
557
|
+
proposedMarketPosition
|
|
558
|
+
);
|
|
559
|
+
|
|
560
|
+
// total position value after trade
|
|
561
|
+
const targetTotalPositionValueUSDC =
|
|
562
|
+
totalCurrentPositionValueIgnoringTargetUSDC.add(
|
|
563
|
+
proposedMarketPositionValueUSDC
|
|
564
|
+
);
|
|
565
|
+
|
|
566
|
+
let totalFreeCollateralUSDC = tc.sub(
|
|
567
|
+
totalCurrentPositionValueIgnoringTargetUSDC
|
|
568
|
+
.mul(TEN_THOUSAND)
|
|
569
|
+
.div(this.getMaxLeverage('Maintenance'))
|
|
570
|
+
);
|
|
571
|
+
|
|
572
|
+
if (partial) {
|
|
573
|
+
totalFreeCollateralUSDC = tc.sub(
|
|
574
|
+
totalCurrentPositionValueIgnoringTargetUSDC
|
|
575
|
+
.mul(TEN_THOUSAND)
|
|
576
|
+
.div(this.getMaxLeverage('Partial'))
|
|
577
|
+
);
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
let priceDelt;
|
|
581
|
+
if (proposedBaseAssetAmount.lt(ZERO)) {
|
|
582
|
+
priceDelt = (tc
|
|
583
|
+
.mul(thisLev)
|
|
584
|
+
.sub(tpv))
|
|
585
|
+
.mul(PRICE_TO_QUOTE_PRECISION)
|
|
586
|
+
.div(thisLev.add(new BN(1)));
|
|
587
|
+
} else {
|
|
588
|
+
priceDelt = (tc
|
|
589
|
+
.mul(thisLev)
|
|
590
|
+
.sub(tpv))
|
|
591
|
+
.mul(PRICE_TO_QUOTE_PRECISION)
|
|
592
|
+
.div(thisLev.sub(new BN(1)));
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
let currentPrice;
|
|
596
|
+
if(positionBaseSizeChange.eq(ZERO)){
|
|
597
|
+
currentPrice = calculateMarkPrice(
|
|
598
|
+
this.clearingHouse.getMarket(targetMarket.marketIndex)
|
|
599
|
+
);
|
|
600
|
+
} else{
|
|
601
|
+
const direction = positionBaseSizeChange.gt(ZERO) ? PositionDirection.LONG : PositionDirection.SHORT;
|
|
602
|
+
currentPrice = calculateTradeSlippage(
|
|
603
|
+
direction,
|
|
604
|
+
positionBaseSizeChange.abs(),
|
|
605
|
+
this.clearingHouse.getMarket(targetMarket.marketIndex),
|
|
606
|
+
'base',
|
|
607
|
+
)[3]; // newPrice after swap
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
|
|
611
|
+
// if the position value after the trade is less than total collateral, there is no liq price
|
|
612
|
+
if (
|
|
613
|
+
targetTotalPositionValueUSDC.lte(totalFreeCollateralUSDC) &&
|
|
614
|
+
proposedMarketPosition.baseAssetAmount.gt(ZERO)
|
|
615
|
+
) {
|
|
616
|
+
return new BN(-1);
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
if (proposedBaseAssetAmount.eq(ZERO)) return new BN(-1);
|
|
620
|
+
|
|
621
|
+
const eatMargin2 = priceDelt
|
|
622
|
+
.mul(AMM_RESERVE_PRECISION)
|
|
623
|
+
.div(proposedBaseAssetAmount);
|
|
624
|
+
|
|
625
|
+
if(eatMargin2.gt(currentPrice)){
|
|
626
|
+
return new BN(-1);
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
const liqPrice = currentPrice.sub(eatMargin2);
|
|
630
|
+
return liqPrice;
|
|
631
|
+
}
|
|
632
|
+
|
|
482
633
|
/**
|
|
483
634
|
* Calculates the estimated liquidation price for a position after closing a quote amount of the position.
|
|
484
635
|
* @param positionMarketIndex
|
|
@@ -489,7 +640,9 @@ export class ClearingHouseUser {
|
|
|
489
640
|
positionMarketIndex: BN,
|
|
490
641
|
closeQuoteAmount: BN
|
|
491
642
|
): BN {
|
|
492
|
-
const currentPosition =
|
|
643
|
+
const currentPosition =
|
|
644
|
+
this.getUserPosition(positionMarketIndex) ||
|
|
645
|
+
this.getEmptyPosition(positionMarketIndex);
|
|
493
646
|
|
|
494
647
|
const closeBaseAmount = currentPosition.baseAssetAmount
|
|
495
648
|
.mul(closeQuoteAmount)
|
|
@@ -511,6 +664,21 @@ export class ClearingHouseUser {
|
|
|
511
664
|
|
|
512
665
|
/**
|
|
513
666
|
* Get the maximum trade size for a given market, taking into account the user's current leverage, positions, collateral, etc.
|
|
667
|
+
*
|
|
668
|
+
* To Calculate Max Quote Available:
|
|
669
|
+
*
|
|
670
|
+
* Case 1: SameSide
|
|
671
|
+
* => Remaining quote to get to maxLeverage
|
|
672
|
+
*
|
|
673
|
+
* Case 2: NOT SameSide && currentLeverage <= maxLeverage
|
|
674
|
+
* => Current opposite position x2 + remaining to get to maxLeverage
|
|
675
|
+
*
|
|
676
|
+
* Case 3: NOT SameSide && currentLeverage > maxLeverage && otherPositions - currentPosition > maxLeverage
|
|
677
|
+
* => strictly reduce current position size
|
|
678
|
+
*
|
|
679
|
+
* Case 4: NOT SameSide && currentLeverage > maxLeverage && otherPositions - currentPosition < maxLeverage
|
|
680
|
+
* => current position + remaining to get to maxLeverage
|
|
681
|
+
*
|
|
514
682
|
* @param marketIndex
|
|
515
683
|
* @param tradeSide
|
|
516
684
|
* @param userMaxLeverageSetting - leverage : Precision TEN_THOUSAND
|
|
@@ -521,32 +689,28 @@ export class ClearingHouseUser {
|
|
|
521
689
|
tradeSide: PositionDirection,
|
|
522
690
|
userMaxLeverageSetting: BN
|
|
523
691
|
): BN {
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
692
|
+
const currentPosition =
|
|
693
|
+
this.getUserPosition(targetMarketIndex) ||
|
|
694
|
+
this.getEmptyPosition(targetMarketIndex);
|
|
527
695
|
|
|
528
|
-
|
|
696
|
+
const targetSide = tradeSide === PositionDirection.SHORT ? 'short' : 'long';
|
|
529
697
|
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
side === 'short' &&
|
|
534
|
-
!currentPosition?.baseAssetAmount.isNeg()
|
|
535
|
-
) {
|
|
536
|
-
return this.getPositionValue(targetMarketIndex);
|
|
537
|
-
}
|
|
698
|
+
const currentPositionSide = currentPosition?.baseAssetAmount.isNeg()
|
|
699
|
+
? 'short'
|
|
700
|
+
: 'long';
|
|
538
701
|
|
|
539
|
-
|
|
540
|
-
|
|
702
|
+
const targettingSameSide = !currentPosition
|
|
703
|
+
? true
|
|
704
|
+
: targetSide === currentPositionSide;
|
|
541
705
|
|
|
542
|
-
|
|
706
|
+
// add any position we have on the opposite side of the current trade, because we can "flip" the size of this position without taking any extra leverage.
|
|
707
|
+
const oppositeSizeValueUSDC = targettingSameSide
|
|
708
|
+
? ZERO
|
|
709
|
+
: this.getPositionValue(targetMarketIndex);
|
|
543
710
|
|
|
544
711
|
// get current leverage
|
|
545
712
|
const currentLeverage = this.getLeverage();
|
|
546
713
|
|
|
547
|
-
// remaining leverage
|
|
548
|
-
// let remainingLeverage = userMaxLeverageSetting;
|
|
549
|
-
|
|
550
714
|
const remainingLeverage = BN.max(
|
|
551
715
|
userMaxLeverageSetting.sub(currentLeverage),
|
|
552
716
|
ZERO
|
|
@@ -560,10 +724,57 @@ export class ClearingHouseUser {
|
|
|
560
724
|
.mul(totalCollateral)
|
|
561
725
|
.div(TEN_THOUSAND);
|
|
562
726
|
|
|
563
|
-
|
|
564
|
-
|
|
727
|
+
if (userMaxLeverageSetting.sub(currentLeverage).gte(ZERO)) {
|
|
728
|
+
if (oppositeSizeValueUSDC.eq(ZERO)) {
|
|
729
|
+
// case 1 : Regular trade where current total position less than max, and no opposite position to account for
|
|
730
|
+
// do nothing
|
|
731
|
+
} else {
|
|
732
|
+
// case 2 : trade where current total position less than max, but need to account for flipping the current position over to the other side
|
|
733
|
+
maxPositionSize = maxPositionSize.add(
|
|
734
|
+
oppositeSizeValueUSDC.mul(new BN(2))
|
|
735
|
+
);
|
|
736
|
+
}
|
|
737
|
+
} else {
|
|
738
|
+
// current leverage is greater than max leverage - can only reduce position size
|
|
739
|
+
|
|
740
|
+
if (!targettingSameSide) {
|
|
741
|
+
const currentPositionQuoteSize =
|
|
742
|
+
this.getPositionValue(targetMarketIndex);
|
|
743
|
+
|
|
744
|
+
const currentTotalQuoteSize = currentLeverage
|
|
745
|
+
.mul(totalCollateral)
|
|
746
|
+
.div(TEN_THOUSAND);
|
|
565
747
|
|
|
566
|
-
|
|
748
|
+
const otherPositionsTotalQuoteSize = currentTotalQuoteSize.sub(
|
|
749
|
+
currentPositionQuoteSize
|
|
750
|
+
);
|
|
751
|
+
|
|
752
|
+
const quoteValueOfMaxLeverage = userMaxLeverageSetting
|
|
753
|
+
.mul(totalCollateral)
|
|
754
|
+
.div(TEN_THOUSAND);
|
|
755
|
+
|
|
756
|
+
if (
|
|
757
|
+
otherPositionsTotalQuoteSize
|
|
758
|
+
.sub(currentPositionQuoteSize)
|
|
759
|
+
.gte(quoteValueOfMaxLeverage)
|
|
760
|
+
) {
|
|
761
|
+
// case 3: Can only reduce the current position because it will still be greater than max leverage
|
|
762
|
+
|
|
763
|
+
maxPositionSize = currentPositionQuoteSize;
|
|
764
|
+
} else {
|
|
765
|
+
// case 4: Can reduce the position, and then take extra remaining quote to get to max leverage
|
|
766
|
+
|
|
767
|
+
const allowedQuoteSizeAfterClosingCurrentPosition =
|
|
768
|
+
quoteValueOfMaxLeverage.sub(otherPositionsTotalQuoteSize);
|
|
769
|
+
|
|
770
|
+
maxPositionSize = currentPositionQuoteSize.add(
|
|
771
|
+
allowedQuoteSizeAfterClosingCurrentPosition
|
|
772
|
+
);
|
|
773
|
+
}
|
|
774
|
+
} else {
|
|
775
|
+
// do nothing if targetting same side
|
|
776
|
+
}
|
|
777
|
+
}
|
|
567
778
|
|
|
568
779
|
// subtract oneMillionth of maxPositionSize
|
|
569
780
|
// => to avoid rounding errors when taking max leverage
|
|
@@ -585,12 +796,16 @@ export class ClearingHouseUser {
|
|
|
585
796
|
tradeQuoteAmount: BN,
|
|
586
797
|
tradeSide: PositionDirection
|
|
587
798
|
): BN {
|
|
588
|
-
const currentPosition =
|
|
589
|
-
|
|
799
|
+
const currentPosition =
|
|
800
|
+
this.getUserPosition(targetMarketIndex) ||
|
|
801
|
+
this.getEmptyPosition(targetMarketIndex);
|
|
590
802
|
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
803
|
+
let currentPositionQuoteAmount = this.getPositionValue(targetMarketIndex);
|
|
804
|
+
|
|
805
|
+
const currentSide =
|
|
806
|
+
currentPosition && currentPosition.baseAssetAmount.isNeg()
|
|
807
|
+
? PositionDirection.SHORT
|
|
808
|
+
: PositionDirection.LONG;
|
|
594
809
|
|
|
595
810
|
if (currentSide === PositionDirection.SHORT)
|
|
596
811
|
currentPositionQuoteAmount = currentPositionQuoteAmount.neg();
|
|
@@ -605,11 +820,18 @@ export class ClearingHouseUser {
|
|
|
605
820
|
const totalPositionAfterTradeExcludingTargetMarket =
|
|
606
821
|
this.getTotalPositionValueExcludingMarket(targetMarketIndex);
|
|
607
822
|
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
823
|
+
const totalCollateral = this.getTotalCollateral();
|
|
824
|
+
|
|
825
|
+
if (totalCollateral.gt(ZERO)) {
|
|
826
|
+
const newLeverage = currentMarketPositionAfterTrade
|
|
827
|
+
.add(totalPositionAfterTradeExcludingTargetMarket)
|
|
828
|
+
.abs()
|
|
829
|
+
.mul(TEN_THOUSAND)
|
|
830
|
+
.div(totalCollateral);
|
|
831
|
+
return newLeverage;
|
|
832
|
+
} else {
|
|
833
|
+
return new BN(0);
|
|
834
|
+
}
|
|
613
835
|
}
|
|
614
836
|
|
|
615
837
|
/**
|
|
@@ -631,17 +853,13 @@ export class ClearingHouseUser {
|
|
|
631
853
|
* @returns positionValue : Precision QUOTE_PRECISION
|
|
632
854
|
*/
|
|
633
855
|
private getTotalPositionValueExcludingMarket(marketToIgnore: BN): BN {
|
|
634
|
-
const currentMarketPosition =
|
|
856
|
+
const currentMarketPosition =
|
|
857
|
+
this.getUserPosition(marketToIgnore) ||
|
|
858
|
+
this.getEmptyPosition(marketToIgnore);
|
|
635
859
|
|
|
636
860
|
let currentMarketPositionValueUSDC = ZERO;
|
|
637
861
|
if (currentMarketPosition) {
|
|
638
|
-
|
|
639
|
-
currentMarketPosition.marketIndex
|
|
640
|
-
);
|
|
641
|
-
currentMarketPositionValueUSDC = calculateBaseAssetValue(
|
|
642
|
-
market,
|
|
643
|
-
currentMarketPosition
|
|
644
|
-
);
|
|
862
|
+
currentMarketPositionValueUSDC = this.getPositionValue(marketToIgnore);
|
|
645
863
|
}
|
|
646
864
|
|
|
647
865
|
return this.getTotalPositionValue().sub(currentMarketPositionValueUSDC);
|
package/src/constants/markets.ts
CHANGED
|
@@ -44,4 +44,25 @@ export const Markets: Market[] = [
|
|
|
44
44
|
devnetPythOracle: 'FVb5h1VmHPfVb1RfqZckchq18GxRv4iKt8T4eVTQAqdz',
|
|
45
45
|
mainnetPythOracle: 'Ax9ujW5B9oqcv59N8m6f1BpTBq2rGeGaBcpKjC5UYsXU',
|
|
46
46
|
},
|
|
47
|
+
{
|
|
48
|
+
symbol: 'BNB-PERP',
|
|
49
|
+
baseAssetSymbol: 'BNB',
|
|
50
|
+
marketIndex: new BN(5),
|
|
51
|
+
devnetPythOracle: 'GwzBgrXb4PG59zjce24SF2b9JXbLEjJJTBkmytuEZj1b',
|
|
52
|
+
mainnetPythOracle: '4CkQJBxhU8EZ2UjhigbtdaPbpTe6mqf811fipYBFbSYN',
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
symbol: 'MATIC-PERP',
|
|
56
|
+
baseAssetSymbol: 'MATIC',
|
|
57
|
+
marketIndex: new BN(6),
|
|
58
|
+
devnetPythOracle: 'FBirwuDFuRAu4iSGc7RGxN5koHB7EJM1wbCmyPuQoGur',
|
|
59
|
+
mainnetPythOracle: '7KVswB9vkCgeM3SHP7aGDijvdRAHK8P5wi9JXViCrtYh',
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
symbol: 'ATOM-PERP',
|
|
63
|
+
baseAssetSymbol: 'ATOM',
|
|
64
|
+
marketIndex: new BN(7),
|
|
65
|
+
devnetPythOracle: '7YAze8qFUMkBnyLVdKT4TFUUFui99EwS5gfRArMcrvFk',
|
|
66
|
+
mainnetPythOracle: 'CrCpTerNqtZvqLcKqz1k13oVeXV9WkMD2zA9hBKXrsbN',
|
|
67
|
+
},
|
|
47
68
|
];
|
|
@@ -19,3 +19,5 @@ export const AMM_TO_QUOTE_PRECISION_RATIO =
|
|
|
19
19
|
AMM_RESERVE_PRECISION.div(QUOTE_PRECISION); // 10^7
|
|
20
20
|
export const PRICE_TO_QUOTE_PRECISION =
|
|
21
21
|
MARK_PRICE_PRECISION.div(QUOTE_PRECISION);
|
|
22
|
+
export const AMM_TIMES_PEG_TO_QUOTE_PRECISION_RATIO =
|
|
23
|
+
AMM_RESERVE_PRECISION.mul(PEG_PRECISION).div(QUOTE_PRECISION); // 10^10
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { BN, Provider
|
|
1
|
+
import { BN, Provider } from '@project-serum/anchor';
|
|
2
|
+
import { Wallet } from '..';
|
|
2
3
|
import { Token, TOKEN_PROGRAM_ID } from '@solana/spl-token';
|
|
3
4
|
import { Connection, Keypair, PublicKey } from '@solana/web3.js';
|
|
4
5
|
import {
|