@drift-labs/sdk 0.1.12 → 0.1.16
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 +31 -3
- package/lib/clearingHouseUser.d.ts.map +1 -1
- package/lib/clearingHouseUser.js +213 -47
- 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 +41 -22
- 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 -13
- 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 +306 -65
- 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 +47 -25
- 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 -14
- 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/accounts/types.ts
CHANGED
|
@@ -16,6 +16,7 @@ import { EventEmitter } from 'events';
|
|
|
16
16
|
export interface AccountSubscriber<T> {
|
|
17
17
|
data?: T;
|
|
18
18
|
subscribe(onChange: (data: T) => void): Promise<void>;
|
|
19
|
+
fetch(): Promise<void>;
|
|
19
20
|
unsubscribe(): void;
|
|
20
21
|
}
|
|
21
22
|
|
|
@@ -54,7 +55,7 @@ export interface ClearingHouseAccountSubscriber {
|
|
|
54
55
|
subscribe(
|
|
55
56
|
optionalSubscriptions?: ClearingHouseAccountTypes[]
|
|
56
57
|
): Promise<boolean>;
|
|
57
|
-
|
|
58
|
+
fetch(): Promise<void>;
|
|
58
59
|
unsubscribe(): Promise<void>;
|
|
59
60
|
|
|
60
61
|
getStateAccount(): StateAccount;
|
|
@@ -67,33 +68,6 @@ export interface ClearingHouseAccountSubscriber {
|
|
|
67
68
|
getLiquidationHistoryAccount(): LiquidationHistoryAccount;
|
|
68
69
|
}
|
|
69
70
|
|
|
70
|
-
export interface HistoryAccountEvents {
|
|
71
|
-
fundingPaymentHistoryAccountUpdate: (
|
|
72
|
-
payload: FundingPaymentHistoryAccount
|
|
73
|
-
) => void;
|
|
74
|
-
fundingRateHistoryAccountUpdate: (payload: FundingRateHistoryAccount) => void;
|
|
75
|
-
tradeHistoryAccountUpdate: (payload: TradeHistoryAccount) => void;
|
|
76
|
-
liquidationHistoryAccountUpdate: (payload: LiquidationHistoryAccount) => void;
|
|
77
|
-
depositHistoryAccountUpdate: (payload: DepositHistoryAccount) => void;
|
|
78
|
-
curveHistoryAccountUpdate: (payload: CurveHistoryAccount) => void;
|
|
79
|
-
update: void;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
export interface HistoryAccountSubscriber {
|
|
83
|
-
eventEmitter: StrictEventEmitter<EventEmitter, HistoryAccountEvents>;
|
|
84
|
-
isSubscribed: boolean;
|
|
85
|
-
|
|
86
|
-
subscribe(): Promise<boolean>;
|
|
87
|
-
unsubscribe(): Promise<void>;
|
|
88
|
-
|
|
89
|
-
getTradeHistoryAccount(): TradeHistoryAccount;
|
|
90
|
-
getDepositHistoryAccount(): DepositHistoryAccount;
|
|
91
|
-
getFundingPaymentHistoryAccount(): FundingPaymentHistoryAccount;
|
|
92
|
-
getFundingRateHistoryAccount(): FundingRateHistoryAccount;
|
|
93
|
-
getCurveHistoryAccount(): CurveHistoryAccount;
|
|
94
|
-
getLiquidationHistoryAccount(): LiquidationHistoryAccount;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
71
|
export interface UserAccountEvents {
|
|
98
72
|
userAccountData: (payload: UserAccount) => void;
|
|
99
73
|
userPositionsData: (payload: UserPositionsAccount) => void;
|
|
@@ -105,6 +79,7 @@ export interface UserAccountSubscriber {
|
|
|
105
79
|
isSubscribed: boolean;
|
|
106
80
|
|
|
107
81
|
subscribe(): Promise<boolean>;
|
|
82
|
+
fetch(): Promise<void>;
|
|
108
83
|
unsubscribe(): Promise<void>;
|
|
109
84
|
|
|
110
85
|
getUserAccount(): UserAccount;
|
|
@@ -7,6 +7,7 @@ export class WebSocketAccountSubscriber<T> implements AccountSubscriber<T> {
|
|
|
7
7
|
accountName: string;
|
|
8
8
|
program: Program;
|
|
9
9
|
accountPublicKey: PublicKey;
|
|
10
|
+
onChange: (data: T) => void;
|
|
10
11
|
|
|
11
12
|
public constructor(
|
|
12
13
|
accountName: string,
|
|
@@ -19,20 +20,29 @@ export class WebSocketAccountSubscriber<T> implements AccountSubscriber<T> {
|
|
|
19
20
|
}
|
|
20
21
|
|
|
21
22
|
async subscribe(onChange: (data: T) => void): Promise<void> {
|
|
22
|
-
this.
|
|
23
|
-
|
|
24
|
-
)) as T;
|
|
25
|
-
|
|
26
|
-
onChange(this.data);
|
|
23
|
+
this.onChange = onChange;
|
|
24
|
+
await this.fetch();
|
|
27
25
|
|
|
28
26
|
this.program.account[this.accountName]
|
|
29
27
|
.subscribe(this.accountPublicKey, this.program.provider.opts.commitment)
|
|
30
28
|
.on('change', async (data: T) => {
|
|
31
29
|
this.data = data;
|
|
32
|
-
onChange(data);
|
|
30
|
+
this.onChange(data);
|
|
33
31
|
});
|
|
34
32
|
}
|
|
35
33
|
|
|
34
|
+
async fetch(): Promise<void> {
|
|
35
|
+
const newData = (await this.program.account[this.accountName].fetch(
|
|
36
|
+
this.accountPublicKey
|
|
37
|
+
)) as T;
|
|
38
|
+
|
|
39
|
+
// if data has changed trigger update
|
|
40
|
+
if (JSON.stringify(newData) !== JSON.stringify(this.data)) {
|
|
41
|
+
this.data = newData;
|
|
42
|
+
this.onChange(this.data);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
36
46
|
unsubscribe(): Promise<void> {
|
|
37
47
|
return this.program.account[this.accountName].unsubscribe(
|
|
38
48
|
this.accountPublicKey
|
package/src/clearingHouse.ts
CHANGED
|
@@ -16,7 +16,6 @@ import {
|
|
|
16
16
|
PositionDirection,
|
|
17
17
|
TradeHistoryAccount,
|
|
18
18
|
UserAccount,
|
|
19
|
-
UserPositionsAccount,
|
|
20
19
|
Market,
|
|
21
20
|
} from './types';
|
|
22
21
|
import * as anchor from '@project-serum/anchor';
|
|
@@ -140,6 +139,13 @@ export class ClearingHouse {
|
|
|
140
139
|
]);
|
|
141
140
|
}
|
|
142
141
|
|
|
142
|
+
/**
|
|
143
|
+
* Forces the accountSubscriber to fetch account updates from rpc
|
|
144
|
+
*/
|
|
145
|
+
public async fetchAccounts(): Promise<void> {
|
|
146
|
+
await this.accountSubscriber.fetch();
|
|
147
|
+
}
|
|
148
|
+
|
|
143
149
|
/**
|
|
144
150
|
* Unsubscribe from all currently subscribed state accounts
|
|
145
151
|
*/
|
|
@@ -454,7 +460,7 @@ export class ClearingHouse {
|
|
|
454
460
|
collateralAccountPublicKey: PublicKey
|
|
455
461
|
): Promise<TransactionInstruction> {
|
|
456
462
|
const userAccountPublicKey = await this.getUserAccountPublicKey();
|
|
457
|
-
const user:
|
|
463
|
+
const user: any = await this.program.account.user.fetch(
|
|
458
464
|
userAccountPublicKey
|
|
459
465
|
);
|
|
460
466
|
|
|
@@ -659,7 +665,7 @@ export class ClearingHouse {
|
|
|
659
665
|
const liquidateeUserAccount: any = await this.program.account.user.fetch(
|
|
660
666
|
liquidateeUserAccountPublicKey
|
|
661
667
|
);
|
|
662
|
-
const liquidateePositions:
|
|
668
|
+
const liquidateePositions: any =
|
|
663
669
|
await this.program.account.userPositions.fetch(
|
|
664
670
|
liquidateeUserAccount.positions
|
|
665
671
|
);
|
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;
|
|
@@ -205,7 +218,8 @@ export class ClearingHouseUser {
|
|
|
205
218
|
* @returns : Precision QUOTE_PRECISION
|
|
206
219
|
*/
|
|
207
220
|
public getPositionValue(marketIndex: BN): BN {
|
|
208
|
-
const userPosition =
|
|
221
|
+
const userPosition =
|
|
222
|
+
this.getUserPosition(marketIndex) || this.getEmptyPosition(marketIndex);
|
|
209
223
|
const market = this.clearingHouse.getMarket(userPosition.marketIndex);
|
|
210
224
|
return calculateBaseAssetValue(market, userPosition);
|
|
211
225
|
}
|
|
@@ -226,15 +240,17 @@ export class ClearingHouseUser {
|
|
|
226
240
|
* calculates average exit price for closing 100% of position
|
|
227
241
|
* @returns : Precision MARK_PRICE_PRECISION
|
|
228
242
|
*/
|
|
229
|
-
public
|
|
243
|
+
public getPositionEstimatedExitPriceAndPnl(
|
|
230
244
|
position: UserPosition,
|
|
231
245
|
amountToClose?: BN
|
|
232
|
-
): BN {
|
|
246
|
+
): [BN, BN] {
|
|
233
247
|
const market = this.clearingHouse.getMarket(position.marketIndex);
|
|
234
248
|
|
|
249
|
+
const entryPrice = calculateEntryPrice(position);
|
|
250
|
+
|
|
235
251
|
if (amountToClose) {
|
|
236
252
|
if (amountToClose.eq(ZERO)) {
|
|
237
|
-
return calculateMarkPrice(market);
|
|
253
|
+
return [calculateMarkPrice(market), ZERO];
|
|
238
254
|
}
|
|
239
255
|
position = {
|
|
240
256
|
baseAssetAmount: amountToClose,
|
|
@@ -246,12 +262,21 @@ export class ClearingHouseUser {
|
|
|
246
262
|
|
|
247
263
|
const baseAssetValue = calculateBaseAssetValue(market, position);
|
|
248
264
|
if (position.baseAssetAmount.eq(ZERO)) {
|
|
249
|
-
return ZERO;
|
|
265
|
+
return [ZERO, ZERO];
|
|
250
266
|
}
|
|
251
|
-
|
|
267
|
+
|
|
268
|
+
const exitPrice = baseAssetValue
|
|
252
269
|
.mul(AMM_TO_QUOTE_PRECISION_RATIO)
|
|
253
270
|
.mul(MARK_PRICE_PRECISION)
|
|
254
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];
|
|
255
280
|
}
|
|
256
281
|
|
|
257
282
|
/**
|
|
@@ -350,7 +375,7 @@ export class ClearingHouseUser {
|
|
|
350
375
|
* @param partial
|
|
351
376
|
* @returns Precision : MARK_PRICE_PRECISION
|
|
352
377
|
*/
|
|
353
|
-
public
|
|
378
|
+
public liquidationPriceOld(
|
|
354
379
|
targetMarket: Pick<UserPosition, 'marketIndex'>,
|
|
355
380
|
positionBaseSizeChange: BN = ZERO,
|
|
356
381
|
partial = false
|
|
@@ -379,13 +404,11 @@ export class ClearingHouseUser {
|
|
|
379
404
|
const totalCurrentPositionValueIgnoringTargetUSDC =
|
|
380
405
|
this.getTotalPositionValueExcludingMarket(targetMarket.marketIndex);
|
|
381
406
|
|
|
382
|
-
const currentMarketPosition =
|
|
383
|
-
targetMarket.marketIndex
|
|
384
|
-
|
|
407
|
+
const currentMarketPosition =
|
|
408
|
+
this.getUserPosition(targetMarket.marketIndex) ||
|
|
409
|
+
this.getEmptyPosition(targetMarket.marketIndex);
|
|
385
410
|
|
|
386
|
-
const currentMarketPositionBaseSize = currentMarketPosition
|
|
387
|
-
? currentMarketPosition.baseAssetAmount
|
|
388
|
-
: ZERO;
|
|
411
|
+
const currentMarketPositionBaseSize = currentMarketPosition.baseAssetAmount;
|
|
389
412
|
|
|
390
413
|
// calculate position for current market after trade
|
|
391
414
|
const proposedMarketPosition: UserPosition = {
|
|
@@ -412,9 +435,23 @@ export class ClearingHouseUser {
|
|
|
412
435
|
proposedMarketPositionValueUSDC
|
|
413
436
|
);
|
|
414
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
|
+
|
|
415
452
|
// if the position value after the trade is less than total collateral, there is no liq price
|
|
416
453
|
if (
|
|
417
|
-
targetTotalPositionValueUSDC.lte(
|
|
454
|
+
targetTotalPositionValueUSDC.lte(totalFreeCollateralUSDC) &&
|
|
418
455
|
proposedMarketPosition.baseAssetAmount.gt(ZERO)
|
|
419
456
|
) {
|
|
420
457
|
return new BN(-1);
|
|
@@ -456,6 +493,143 @@ export class ClearingHouseUser {
|
|
|
456
493
|
return liqPrice;
|
|
457
494
|
}
|
|
458
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
|
+
|
|
459
633
|
/**
|
|
460
634
|
* Calculates the estimated liquidation price for a position after closing a quote amount of the position.
|
|
461
635
|
* @param positionMarketIndex
|
|
@@ -466,7 +640,9 @@ export class ClearingHouseUser {
|
|
|
466
640
|
positionMarketIndex: BN,
|
|
467
641
|
closeQuoteAmount: BN
|
|
468
642
|
): BN {
|
|
469
|
-
const currentPosition =
|
|
643
|
+
const currentPosition =
|
|
644
|
+
this.getUserPosition(positionMarketIndex) ||
|
|
645
|
+
this.getEmptyPosition(positionMarketIndex);
|
|
470
646
|
|
|
471
647
|
const closeBaseAmount = currentPosition.baseAssetAmount
|
|
472
648
|
.mul(closeQuoteAmount)
|
|
@@ -488,6 +664,21 @@ export class ClearingHouseUser {
|
|
|
488
664
|
|
|
489
665
|
/**
|
|
490
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
|
+
*
|
|
491
682
|
* @param marketIndex
|
|
492
683
|
* @param tradeSide
|
|
493
684
|
* @param userMaxLeverageSetting - leverage : Precision TEN_THOUSAND
|
|
@@ -498,32 +689,28 @@ export class ClearingHouseUser {
|
|
|
498
689
|
tradeSide: PositionDirection,
|
|
499
690
|
userMaxLeverageSetting: BN
|
|
500
691
|
): BN {
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
692
|
+
const currentPosition =
|
|
693
|
+
this.getUserPosition(targetMarketIndex) ||
|
|
694
|
+
this.getEmptyPosition(targetMarketIndex);
|
|
504
695
|
|
|
505
|
-
|
|
696
|
+
const targetSide = tradeSide === PositionDirection.SHORT ? 'short' : 'long';
|
|
506
697
|
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
side === 'short' &&
|
|
511
|
-
!currentPosition?.baseAssetAmount.isNeg()
|
|
512
|
-
) {
|
|
513
|
-
return this.getPositionValue(targetMarketIndex);
|
|
514
|
-
}
|
|
698
|
+
const currentPositionSide = currentPosition?.baseAssetAmount.isNeg()
|
|
699
|
+
? 'short'
|
|
700
|
+
: 'long';
|
|
515
701
|
|
|
516
|
-
|
|
517
|
-
|
|
702
|
+
const targettingSameSide = !currentPosition
|
|
703
|
+
? true
|
|
704
|
+
: targetSide === currentPositionSide;
|
|
518
705
|
|
|
519
|
-
|
|
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);
|
|
520
710
|
|
|
521
711
|
// get current leverage
|
|
522
712
|
const currentLeverage = this.getLeverage();
|
|
523
713
|
|
|
524
|
-
// remaining leverage
|
|
525
|
-
// let remainingLeverage = userMaxLeverageSetting;
|
|
526
|
-
|
|
527
714
|
const remainingLeverage = BN.max(
|
|
528
715
|
userMaxLeverageSetting.sub(currentLeverage),
|
|
529
716
|
ZERO
|
|
@@ -537,10 +724,57 @@ export class ClearingHouseUser {
|
|
|
537
724
|
.mul(totalCollateral)
|
|
538
725
|
.div(TEN_THOUSAND);
|
|
539
726
|
|
|
540
|
-
|
|
541
|
-
|
|
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);
|
|
747
|
+
|
|
748
|
+
const otherPositionsTotalQuoteSize = currentTotalQuoteSize.sub(
|
|
749
|
+
currentPositionQuoteSize
|
|
750
|
+
);
|
|
542
751
|
|
|
543
|
-
|
|
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
|
+
}
|
|
544
778
|
|
|
545
779
|
// subtract oneMillionth of maxPositionSize
|
|
546
780
|
// => to avoid rounding errors when taking max leverage
|
|
@@ -562,12 +796,16 @@ export class ClearingHouseUser {
|
|
|
562
796
|
tradeQuoteAmount: BN,
|
|
563
797
|
tradeSide: PositionDirection
|
|
564
798
|
): BN {
|
|
565
|
-
const currentPosition =
|
|
566
|
-
|
|
799
|
+
const currentPosition =
|
|
800
|
+
this.getUserPosition(targetMarketIndex) ||
|
|
801
|
+
this.getEmptyPosition(targetMarketIndex);
|
|
802
|
+
|
|
803
|
+
let currentPositionQuoteAmount = this.getPositionValue(targetMarketIndex);
|
|
567
804
|
|
|
568
|
-
const currentSide =
|
|
569
|
-
|
|
570
|
-
|
|
805
|
+
const currentSide =
|
|
806
|
+
currentPosition && currentPosition.baseAssetAmount.isNeg()
|
|
807
|
+
? PositionDirection.SHORT
|
|
808
|
+
: PositionDirection.LONG;
|
|
571
809
|
|
|
572
810
|
if (currentSide === PositionDirection.SHORT)
|
|
573
811
|
currentPositionQuoteAmount = currentPositionQuoteAmount.neg();
|
|
@@ -582,11 +820,18 @@ export class ClearingHouseUser {
|
|
|
582
820
|
const totalPositionAfterTradeExcludingTargetMarket =
|
|
583
821
|
this.getTotalPositionValueExcludingMarket(targetMarketIndex);
|
|
584
822
|
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
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
|
+
}
|
|
590
835
|
}
|
|
591
836
|
|
|
592
837
|
/**
|
|
@@ -608,17 +853,13 @@ export class ClearingHouseUser {
|
|
|
608
853
|
* @returns positionValue : Precision QUOTE_PRECISION
|
|
609
854
|
*/
|
|
610
855
|
private getTotalPositionValueExcludingMarket(marketToIgnore: BN): BN {
|
|
611
|
-
const currentMarketPosition =
|
|
856
|
+
const currentMarketPosition =
|
|
857
|
+
this.getUserPosition(marketToIgnore) ||
|
|
858
|
+
this.getEmptyPosition(marketToIgnore);
|
|
612
859
|
|
|
613
860
|
let currentMarketPositionValueUSDC = ZERO;
|
|
614
861
|
if (currentMarketPosition) {
|
|
615
|
-
|
|
616
|
-
currentMarketPosition.marketIndex
|
|
617
|
-
);
|
|
618
|
-
currentMarketPositionValueUSDC = calculateBaseAssetValue(
|
|
619
|
-
market,
|
|
620
|
-
currentMarketPosition
|
|
621
|
-
);
|
|
862
|
+
currentMarketPositionValueUSDC = this.getPositionValue(marketToIgnore);
|
|
622
863
|
}
|
|
623
864
|
|
|
624
865
|
return this.getTotalPositionValue().sub(currentMarketPositionValueUSDC);
|
package/src/constants/markets.ts
CHANGED
|
@@ -51,4 +51,25 @@ export const Markets: Market[] = [
|
|
|
51
51
|
devnetPythOracle: 'GwzBgrXb4PG59zjce24SF2b9JXbLEjJJTBkmytuEZj1b',
|
|
52
52
|
mainnetPythOracle: '4CkQJBxhU8EZ2UjhigbtdaPbpTe6mqf811fipYBFbSYN',
|
|
53
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
|
+
},
|
|
68
|
+
{
|
|
69
|
+
symbol: 'DOT-PERP',
|
|
70
|
+
baseAssetSymbol: 'DOT',
|
|
71
|
+
marketIndex: new BN(8),
|
|
72
|
+
devnetPythOracle: '4dqq5VBpN4EwYb7wyywjjfknvMKu7m78j9mKZRXTj462',
|
|
73
|
+
mainnetPythOracle: 'EcV1X1gY2yb4KXxjVQtTHTbioum2gvmPnFk4zYAt7zne',
|
|
74
|
+
},
|
|
54
75
|
];
|