@drift-labs/sdk 0.2.0-temp.2 → 2.0.0
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 +27 -29
- package/lib/accounts/bulkAccountLoader.d.ts +2 -0
- package/lib/accounts/bulkAccountLoader.js +36 -29
- package/lib/accounts/bulkUserStatsSubscription.d.ts +7 -0
- package/lib/accounts/bulkUserStatsSubscription.js +21 -0
- package/lib/accounts/bulkUserSubscription.d.ts +2 -2
- package/lib/accounts/bulkUserSubscription.js +0 -1
- package/lib/accounts/fetch.d.ts +2 -1
- package/lib/accounts/fetch.js +9 -1
- package/lib/accounts/{pollingClearingHouseAccountSubscriber.d.ts → pollingDriftClientAccountSubscriber.d.ts} +20 -25
- package/lib/accounts/{pollingClearingHouseAccountSubscriber.js → pollingDriftClientAccountSubscriber.js} +45 -49
- package/lib/accounts/{pollingOracleSubscriber.d.ts → pollingOracleAccountSubscriber.d.ts} +2 -2
- package/lib/accounts/{pollingOracleSubscriber.js → pollingOracleAccountSubscriber.js} +3 -3
- package/lib/accounts/pollingUserAccountSubscriber.d.ts +1 -1
- package/lib/accounts/pollingUserAccountSubscriber.js +5 -11
- package/lib/accounts/pollingUserStatsAccountSubscriber.d.ts +27 -0
- package/lib/accounts/pollingUserStatsAccountSubscriber.js +107 -0
- package/lib/accounts/types.d.ts +26 -15
- package/lib/accounts/webSocketDriftClientAccountSubscriber.d.ts +49 -0
- package/lib/accounts/{webSocketClearingHouseAccountSubscriber.js → webSocketDriftClientAccountSubscriber.js} +47 -45
- package/lib/accounts/webSocketUserStatsAccountSubsriber.d.ts +20 -0
- package/lib/accounts/webSocketUserStatsAccountSubsriber.js +47 -0
- package/lib/addresses/marketAddresses.d.ts +1 -3
- package/lib/addresses/marketAddresses.js +1 -1
- package/lib/addresses/pda.d.ts +15 -9
- package/lib/addresses/pda.js +73 -35
- package/lib/adminClient.d.ts +65 -0
- package/lib/adminClient.js +637 -0
- package/lib/config.d.ts +10 -10
- package/lib/config.js +26 -22
- package/lib/constants/numericConstants.d.ts +29 -12
- package/lib/constants/numericConstants.js +40 -21
- package/lib/constants/perpMarkets.d.ts +18 -0
- package/lib/constants/{markets.js → perpMarkets.js} +20 -9
- package/lib/constants/spotMarkets.d.ts +19 -0
- package/lib/constants/spotMarkets.js +63 -0
- package/lib/dlob/DLOB.d.ts +82 -0
- package/lib/dlob/DLOB.js +694 -0
- package/lib/dlob/DLOBNode.d.ts +54 -0
- package/lib/dlob/DLOBNode.js +77 -0
- package/lib/dlob/NodeList.d.ts +27 -0
- package/lib/dlob/NodeList.js +144 -0
- package/lib/driftClient.d.ts +234 -0
- package/lib/driftClient.js +2108 -0
- package/lib/{clearingHouseConfig.d.ts → driftClientConfig.d.ts} +9 -9
- package/lib/{clearingHouseConfig.js → driftClientConfig.js} +0 -0
- package/lib/events/eventList.d.ts +0 -1
- package/lib/events/eventList.js +0 -7
- package/lib/events/eventSubscriber.d.ts +5 -2
- package/lib/events/eventSubscriber.js +25 -11
- package/lib/events/fetchLogs.d.ts +13 -2
- package/lib/events/fetchLogs.js +45 -14
- package/lib/events/pollingLogProvider.d.ts +2 -1
- package/lib/events/pollingLogProvider.js +7 -3
- package/lib/events/sort.js +8 -11
- package/lib/events/txEventCache.d.ts +0 -2
- package/lib/events/txEventCache.js +0 -14
- package/lib/events/types.d.ts +11 -3
- package/lib/events/types.js +8 -0
- package/lib/events/webSocketLogProvider.js +1 -1
- package/lib/examples/makeTradeExample.js +30 -18
- package/lib/factory/bigNum.d.ts +8 -4
- package/lib/factory/bigNum.js +109 -19
- package/lib/idl/drift.json +8392 -0
- package/lib/index.d.ts +29 -12
- package/lib/index.js +29 -12
- package/lib/math/amm.d.ts +9 -6
- package/lib/math/amm.js +91 -38
- package/lib/math/conversion.js +1 -1
- package/lib/math/exchangeStatus.d.ts +4 -0
- package/lib/math/exchangeStatus.js +18 -0
- package/lib/math/funding.d.ts +6 -6
- package/lib/math/funding.js +23 -21
- package/lib/math/insurance.d.ts +4 -0
- package/lib/math/insurance.js +27 -0
- package/lib/math/margin.d.ts +11 -0
- package/lib/math/margin.js +82 -0
- package/lib/math/market.d.ts +14 -9
- package/lib/math/market.js +70 -10
- package/lib/math/oracles.d.ts +4 -0
- package/lib/math/oracles.js +36 -8
- package/lib/math/orders.d.ts +16 -6
- package/lib/math/orders.js +97 -17
- package/lib/math/position.d.ts +27 -13
- package/lib/math/position.js +91 -37
- package/lib/math/repeg.js +17 -8
- package/lib/math/spotBalance.d.ts +22 -0
- package/lib/math/spotBalance.js +192 -0
- package/lib/math/spotMarket.d.ts +4 -0
- package/lib/math/spotMarket.js +8 -0
- package/lib/math/spotPosition.d.ts +6 -0
- package/lib/math/spotPosition.js +23 -0
- package/lib/math/trade.d.ts +10 -10
- package/lib/math/trade.js +27 -31
- package/lib/oracles/pythClient.js +1 -1
- package/lib/oracles/quoteAssetOracleClient.js +1 -1
- package/lib/oracles/switchboardClient.js +1 -1
- package/lib/orderParams.d.ts +4 -4
- package/lib/orderParams.js +12 -4
- package/lib/serum/serumFulfillmentConfigMap.d.ts +10 -0
- package/lib/serum/serumFulfillmentConfigMap.js +17 -0
- package/lib/serum/serumSubscriber.d.ts +27 -0
- package/lib/serum/serumSubscriber.js +56 -0
- package/lib/serum/types.d.ts +11 -0
- package/{src/oracles → lib/serum}/types.js +0 -0
- package/lib/tokenFaucet.d.ts +1 -0
- package/lib/tokenFaucet.js +23 -12
- package/lib/tx/retryTxSender.d.ts +1 -1
- package/lib/tx/retryTxSender.js +13 -4
- package/lib/tx/types.d.ts +1 -1
- package/lib/types.d.ts +631 -222
- package/lib/types.js +137 -24
- package/lib/user.d.ts +228 -0
- package/lib/user.js +959 -0
- package/lib/userConfig.d.ts +14 -0
- package/lib/{clearingHouseUserConfig.js → userConfig.js} +0 -0
- package/lib/userMap/userMap.d.ts +41 -0
- package/lib/userMap/userMap.js +85 -0
- package/lib/userMap/userStatsMap.d.ts +19 -0
- package/lib/userMap/userStatsMap.js +68 -0
- package/lib/userName.d.ts +1 -0
- package/lib/userName.js +3 -2
- package/lib/userStats.d.ts +18 -0
- package/lib/userStats.js +49 -0
- package/lib/userStatsConfig.d.ts +14 -0
- package/{src/clearingHouseConfig.js → lib/userStatsConfig.js} +0 -0
- package/lib/util/getTokenAddress.d.ts +2 -0
- package/lib/util/getTokenAddress.js +9 -0
- package/package.json +12 -5
- package/src/accounts/bulkAccountLoader.ts +44 -34
- package/src/accounts/bulkUserStatsSubscription.ts +33 -0
- package/src/accounts/bulkUserSubscription.ts +2 -3
- package/src/accounts/fetch.ts +27 -2
- package/src/accounts/{pollingClearingHouseAccountSubscriber.ts → pollingDriftClientAccountSubscriber.ts} +65 -75
- package/src/accounts/{pollingOracleSubscriber.ts → pollingOracleAccountSubscriber.ts} +2 -2
- package/src/accounts/pollingUserAccountSubscriber.ts +5 -12
- package/src/accounts/pollingUserStatsAccountSubscriber.ts +166 -0
- package/src/accounts/types.ts +35 -15
- package/src/accounts/{webSocketClearingHouseAccountSubscriber.ts → webSocketDriftClientAccountSubscriber.ts} +78 -73
- package/src/accounts/webSocketUserStatsAccountSubsriber.ts +80 -0
- package/src/addresses/marketAddresses.ts +3 -4
- package/src/addresses/pda.ts +105 -33
- package/src/adminClient.ts +1207 -0
- package/src/config.ts +41 -34
- package/src/constants/numericConstants.ts +59 -26
- package/src/constants/{markets.ts → perpMarkets.ts} +22 -11
- package/src/constants/spotMarkets.ts +83 -0
- package/src/dlob/DLOB.ts +1120 -0
- package/src/dlob/DLOBNode.ts +155 -0
- package/src/dlob/NodeList.ts +195 -0
- package/src/driftClient.ts +3594 -0
- package/src/{clearingHouseConfig.ts → driftClientConfig.ts} +9 -8
- package/src/events/eventList.ts +1 -8
- package/src/events/eventSubscriber.ts +36 -14
- package/src/events/fetchLogs.ts +60 -15
- package/src/events/pollingLogProvider.ts +11 -3
- package/src/events/sort.ts +11 -15
- package/src/events/txEventCache.ts +0 -16
- package/src/events/types.ts +27 -2
- package/src/events/webSocketLogProvider.ts +1 -1
- package/src/examples/makeTradeExample.js +152 -75
- package/src/examples/makeTradeExample.ts +44 -28
- package/src/factory/bigNum.ts +150 -22
- package/src/idl/drift.json +8392 -0
- package/src/idl/pyth.json +98 -2
- package/src/index.ts +29 -12
- package/src/math/amm.ts +161 -48
- package/src/math/conversion.ts +2 -2
- package/src/math/exchangeStatus.ts +31 -0
- package/src/math/funding.ts +41 -31
- package/src/math/insurance.ts +35 -0
- package/src/math/margin.ts +133 -0
- package/src/math/market.ts +143 -14
- package/src/math/oracles.ts +63 -9
- package/src/math/orders.ts +168 -26
- package/src/math/position.ts +133 -59
- package/src/math/repeg.ts +19 -9
- package/src/math/spotBalance.ts +319 -0
- package/src/math/spotMarket.ts +9 -0
- package/src/math/spotPosition.ts +47 -0
- package/src/math/trade.ts +33 -37
- package/src/oracles/pythClient.ts +2 -2
- package/src/oracles/quoteAssetOracleClient.ts +2 -2
- package/src/oracles/switchboardClient.ts +2 -2
- package/src/orderParams.ts +16 -8
- package/src/serum/serumFulfillmentConfigMap.ts +26 -0
- package/src/serum/serumSubscriber.ts +99 -0
- package/src/serum/types.ts +13 -0
- package/src/tokenFaucet.ts +38 -15
- package/src/tx/retryTxSender.ts +16 -5
- package/src/tx/types.ts +2 -1
- package/src/types.ts +594 -195
- package/src/user.ts +1599 -0
- package/src/{clearingHouseUserConfig.ts → userConfig.ts} +5 -5
- package/src/userMap/userMap.ts +124 -0
- package/src/userMap/userStatsMap.ts +108 -0
- package/src/userName.ts +2 -1
- package/src/userStats.ts +75 -0
- package/src/userStatsConfig.ts +18 -0
- package/src/util/getTokenAddress.ts +18 -0
- package/tests/bn/test.ts +46 -11
- package/tests/dlob/helpers.ts +619 -0
- package/tests/dlob/test.ts +4586 -0
- package/lib/accounts/webSocketClearingHouseAccountSubscriber.d.ts +0 -49
- package/lib/admin.d.ts +0 -44
- package/lib/admin.js +0 -438
- package/lib/clearingHouse.d.ts +0 -146
- package/lib/clearingHouse.js +0 -1154
- package/lib/clearingHouseUser.d.ts +0 -187
- package/lib/clearingHouseUser.js +0 -634
- package/lib/clearingHouseUserConfig.d.ts +0 -14
- package/lib/constants/banks.d.ts +0 -16
- package/lib/constants/banks.js +0 -41
- package/lib/constants/markets.d.ts +0 -19
- package/lib/idl/clearing_house.json +0 -4464
- package/lib/math/bankBalance.d.ts +0 -9
- package/lib/math/bankBalance.js +0 -75
- package/lib/math/state.d.ts +0 -8
- package/lib/math/state.js +0 -15
- package/lib/orders.d.ts +0 -8
- package/lib/orders.js +0 -134
- package/src/accounts/bulkAccountLoader.js +0 -197
- package/src/accounts/bulkUserSubscription.js +0 -33
- package/src/accounts/pollingClearingHouseAccountSubscriber.js +0 -311
- package/src/accounts/pollingOracleSubscriber.js +0 -93
- package/src/accounts/pollingTokenAccountSubscriber.js +0 -90
- package/src/accounts/pollingUserAccountSubscriber.js +0 -132
- package/src/accounts/types.js +0 -10
- package/src/accounts/utils.js +0 -7
- package/src/accounts/webSocketAccountSubscriber.js +0 -93
- package/src/accounts/webSocketClearingHouseAccountSubscriber.js +0 -233
- package/src/accounts/webSocketUserAccountSubscriber.js +0 -62
- package/src/addresses/marketAddresses.js +0 -26
- package/src/admin.js +0 -517
- package/src/admin.ts +0 -730
- package/src/clearingHouse.ts +0 -1828
- package/src/clearingHouseUser.ts +0 -978
- package/src/clearingHouseUserConfig.js +0 -2
- package/src/config.js +0 -67
- package/src/constants/banks.js +0 -42
- package/src/constants/banks.ts +0 -50
- package/src/constants/markets.js +0 -42
- package/src/constants/numericConstants.js +0 -41
- package/src/events/eventSubscriber.js +0 -139
- package/src/events/fetchLogs.js +0 -50
- package/src/events/pollingLogProvider.js +0 -64
- package/src/events/sort.js +0 -44
- package/src/events/txEventCache.js +0 -71
- package/src/events/types.js +0 -20
- package/src/events/webSocketLogProvider.js +0 -41
- package/src/factory/bigNum.js +0 -390
- package/src/factory/oracleClient.js +0 -20
- package/src/idl/clearing_house.json +0 -4464
- package/src/index.js +0 -69
- package/src/math/amm.js +0 -369
- package/src/math/auction.js +0 -42
- package/src/math/bankBalance.ts +0 -112
- package/src/math/conversion.js +0 -11
- package/src/math/funding.js +0 -248
- package/src/math/oracles.js +0 -26
- package/src/math/repeg.js +0 -128
- package/src/math/state.js +0 -15
- package/src/math/state.ts +0 -14
- package/src/math/trade.js +0 -253
- package/src/math/utils.js +0 -26
- package/src/math/utils.js.map +0 -1
- package/src/mockUSDCFaucet.js +0 -280
- package/src/oracles/oracleClientCache.js +0 -19
- package/src/oracles/pythClient.js +0 -46
- package/src/oracles/quoteAssetOracleClient.js +0 -32
- package/src/oracles/switchboardClient.js +0 -69
- package/src/orderParams.js +0 -20
- package/src/orders.ts +0 -245
- package/src/slot/SlotSubscriber.js +0 -39
- package/src/tokenFaucet.js +0 -189
- package/src/types.js +0 -125
- package/src/userName.js +0 -20
- package/src/wallet.js +0 -35
package/src/clearingHouseUser.ts
DELETED
|
@@ -1,978 +0,0 @@
|
|
|
1
|
-
import { PublicKey } from '@solana/web3.js';
|
|
2
|
-
import { EventEmitter } from 'events';
|
|
3
|
-
import StrictEventEmitter from 'strict-event-emitter-types';
|
|
4
|
-
import { ClearingHouse } from './clearingHouse';
|
|
5
|
-
import {
|
|
6
|
-
isVariant,
|
|
7
|
-
MarginCategory,
|
|
8
|
-
Order,
|
|
9
|
-
UserAccount,
|
|
10
|
-
UserPosition,
|
|
11
|
-
} from './types';
|
|
12
|
-
import { calculateEntryPrice } from './math/position';
|
|
13
|
-
import {
|
|
14
|
-
MARK_PRICE_PRECISION,
|
|
15
|
-
AMM_TO_QUOTE_PRECISION_RATIO,
|
|
16
|
-
ZERO,
|
|
17
|
-
TEN_THOUSAND,
|
|
18
|
-
BN_MAX,
|
|
19
|
-
QUOTE_PRECISION,
|
|
20
|
-
AMM_RESERVE_PRECISION,
|
|
21
|
-
PRICE_TO_QUOTE_PRECISION,
|
|
22
|
-
MARGIN_PRECISION,
|
|
23
|
-
BANK_WEIGHT_PRECISION,
|
|
24
|
-
} from './constants/numericConstants';
|
|
25
|
-
import {
|
|
26
|
-
UserAccountSubscriber,
|
|
27
|
-
UserAccountEvents,
|
|
28
|
-
DataAndSlot,
|
|
29
|
-
} from './accounts/types';
|
|
30
|
-
import {
|
|
31
|
-
calculateMarkPrice,
|
|
32
|
-
calculateBaseAssetValue,
|
|
33
|
-
calculatePositionFundingPNL,
|
|
34
|
-
calculatePositionPNL,
|
|
35
|
-
PositionDirection,
|
|
36
|
-
calculateTradeSlippage,
|
|
37
|
-
BN,
|
|
38
|
-
BankAccount,
|
|
39
|
-
} from '.';
|
|
40
|
-
import { getTokenAmount } from './math/bankBalance';
|
|
41
|
-
import { OraclePriceData } from './oracles/types';
|
|
42
|
-
import { ClearingHouseUserConfig } from './clearingHouseUserConfig';
|
|
43
|
-
import { PollingUserAccountSubscriber } from './accounts/pollingUserAccountSubscriber';
|
|
44
|
-
import { WebSocketUserAccountSubscriber } from './accounts/webSocketUserAccountSubscriber';
|
|
45
|
-
|
|
46
|
-
export class ClearingHouseUser {
|
|
47
|
-
clearingHouse: ClearingHouse;
|
|
48
|
-
userAccountPublicKey: PublicKey;
|
|
49
|
-
accountSubscriber: UserAccountSubscriber;
|
|
50
|
-
_isSubscribed = false;
|
|
51
|
-
eventEmitter: StrictEventEmitter<EventEmitter, UserAccountEvents>;
|
|
52
|
-
|
|
53
|
-
public get isSubscribed() {
|
|
54
|
-
return this._isSubscribed && this.accountSubscriber.isSubscribed;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
public set isSubscribed(val: boolean) {
|
|
58
|
-
this._isSubscribed = val;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
public constructor(config: ClearingHouseUserConfig) {
|
|
62
|
-
this.clearingHouse = config.clearingHouse;
|
|
63
|
-
this.userAccountPublicKey = config.userAccountPublicKey;
|
|
64
|
-
if (config.accountSubscription?.type === 'polling') {
|
|
65
|
-
this.accountSubscriber = new PollingUserAccountSubscriber(
|
|
66
|
-
config.clearingHouse.program,
|
|
67
|
-
config.userAccountPublicKey,
|
|
68
|
-
config.accountSubscription.accountLoader
|
|
69
|
-
);
|
|
70
|
-
} else {
|
|
71
|
-
this.accountSubscriber = new WebSocketUserAccountSubscriber(
|
|
72
|
-
config.clearingHouse.program,
|
|
73
|
-
config.userAccountPublicKey
|
|
74
|
-
);
|
|
75
|
-
}
|
|
76
|
-
this.eventEmitter = this.accountSubscriber.eventEmitter;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
* Subscribe to ClearingHouseUser state accounts
|
|
81
|
-
* @returns SusbcriptionSuccess result
|
|
82
|
-
*/
|
|
83
|
-
public async subscribe(): Promise<boolean> {
|
|
84
|
-
this.isSubscribed = await this.accountSubscriber.subscribe();
|
|
85
|
-
return this.isSubscribed;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
/**
|
|
89
|
-
* Forces the accountSubscriber to fetch account updates from rpc
|
|
90
|
-
*/
|
|
91
|
-
public async fetchAccounts(): Promise<void> {
|
|
92
|
-
await this.accountSubscriber.fetch();
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
public async unsubscribe(): Promise<void> {
|
|
96
|
-
await this.accountSubscriber.unsubscribe();
|
|
97
|
-
this.isSubscribed = false;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
public getUserAccount(): UserAccount {
|
|
101
|
-
return this.accountSubscriber.getUserAccountAndSlot().data;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
public getUserAccountAndSlot(): DataAndSlot<UserAccount> | undefined {
|
|
105
|
-
return this.accountSubscriber.getUserAccountAndSlot();
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
/**
|
|
109
|
-
* Gets the user's current position for a given market. If the user has no position returns undefined
|
|
110
|
-
* @param marketIndex
|
|
111
|
-
* @returns userPosition
|
|
112
|
-
*/
|
|
113
|
-
public getUserPosition(marketIndex: BN): UserPosition | undefined {
|
|
114
|
-
return this.getUserAccount().positions.find((position) =>
|
|
115
|
-
position.marketIndex.eq(marketIndex)
|
|
116
|
-
);
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
public getEmptyPosition(marketIndex: BN): UserPosition {
|
|
120
|
-
return {
|
|
121
|
-
baseAssetAmount: ZERO,
|
|
122
|
-
lastCumulativeFundingRate: ZERO,
|
|
123
|
-
marketIndex,
|
|
124
|
-
quoteAssetAmount: ZERO,
|
|
125
|
-
quoteEntryAmount: ZERO,
|
|
126
|
-
openOrders: ZERO,
|
|
127
|
-
unsettledPnl: ZERO,
|
|
128
|
-
openBids: ZERO,
|
|
129
|
-
openAsks: ZERO,
|
|
130
|
-
};
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
/**
|
|
134
|
-
* @param orderId
|
|
135
|
-
* @returns Order
|
|
136
|
-
*/
|
|
137
|
-
public getOrder(orderId: BN): Order | undefined {
|
|
138
|
-
return this.getUserAccount().orders.find((order) =>
|
|
139
|
-
order.orderId.eq(orderId)
|
|
140
|
-
);
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
/**
|
|
144
|
-
* @param userOrderId
|
|
145
|
-
* @returns Order
|
|
146
|
-
*/
|
|
147
|
-
public getOrderByUserOrderId(userOrderId: number): Order | undefined {
|
|
148
|
-
return this.getUserAccount().orders.find(
|
|
149
|
-
(order) => order.userOrderId === userOrderId
|
|
150
|
-
);
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
public getUserAccountPublicKey(): PublicKey {
|
|
154
|
-
return this.userAccountPublicKey;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
public async exists(): Promise<boolean> {
|
|
158
|
-
const userAccountRPCResponse =
|
|
159
|
-
await this.clearingHouse.connection.getParsedAccountInfo(
|
|
160
|
-
this.userAccountPublicKey
|
|
161
|
-
);
|
|
162
|
-
return userAccountRPCResponse.value !== null;
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
/**
|
|
166
|
-
* calculates Buying Power = FC * MAX_LEVERAGE
|
|
167
|
-
* @returns : Precision QUOTE_PRECISION
|
|
168
|
-
*/
|
|
169
|
-
public getBuyingPower(marketIndex: BN | number): BN {
|
|
170
|
-
return this.getFreeCollateral()
|
|
171
|
-
.mul(this.getMaxLeverage(marketIndex, 'Initial'))
|
|
172
|
-
.div(TEN_THOUSAND);
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
/**
|
|
176
|
-
* calculates Free Collateral = Total collateral - initial margin requirement
|
|
177
|
-
* @returns : Precision QUOTE_PRECISION
|
|
178
|
-
*/
|
|
179
|
-
public getFreeCollateral(): BN {
|
|
180
|
-
const totalCollateral = this.getTotalCollateral();
|
|
181
|
-
const initialMarginRequirement = this.getInitialMarginRequirement();
|
|
182
|
-
const freeCollateral = totalCollateral.sub(initialMarginRequirement);
|
|
183
|
-
return freeCollateral.gte(ZERO) ? freeCollateral : ZERO;
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
public getInitialMarginRequirement(): BN {
|
|
187
|
-
return this.getUserAccount()
|
|
188
|
-
.positions.reduce((marginRequirement, marketPosition) => {
|
|
189
|
-
const market = this.clearingHouse.getMarketAccount(
|
|
190
|
-
marketPosition.marketIndex
|
|
191
|
-
);
|
|
192
|
-
return marginRequirement.add(
|
|
193
|
-
calculateBaseAssetValue(
|
|
194
|
-
market,
|
|
195
|
-
marketPosition,
|
|
196
|
-
this.getOracleDataForMarket(market.marketIndex)
|
|
197
|
-
)
|
|
198
|
-
.mul(new BN(market.marginRatioInitial))
|
|
199
|
-
.div(MARGIN_PRECISION)
|
|
200
|
-
);
|
|
201
|
-
}, ZERO)
|
|
202
|
-
.add(this.getTotalLiability());
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
/**
|
|
206
|
-
* @returns The partial margin requirement in USDC. : QUOTE_PRECISION
|
|
207
|
-
*/
|
|
208
|
-
public getMaintenanceMarginRequirement(): BN {
|
|
209
|
-
return this.getUserAccount()
|
|
210
|
-
.positions.reduce((marginRequirement, marketPosition) => {
|
|
211
|
-
const market = this.clearingHouse.getMarketAccount(
|
|
212
|
-
marketPosition.marketIndex
|
|
213
|
-
);
|
|
214
|
-
return marginRequirement.add(
|
|
215
|
-
calculateBaseAssetValue(
|
|
216
|
-
market,
|
|
217
|
-
marketPosition,
|
|
218
|
-
this.getOracleDataForMarket(market.marketIndex)
|
|
219
|
-
)
|
|
220
|
-
.mul(new BN(market.marginRatioMaintenance))
|
|
221
|
-
.div(MARGIN_PRECISION)
|
|
222
|
-
);
|
|
223
|
-
}, ZERO)
|
|
224
|
-
.add(this.getTotalLiability());
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
/**
|
|
228
|
-
* calculates unrealized position price pnl
|
|
229
|
-
* @returns : Precision QUOTE_PRECISION
|
|
230
|
-
*/
|
|
231
|
-
public getUnrealizedPNL(withFunding?: boolean, marketIndex?: BN): BN {
|
|
232
|
-
return this.getUserAccount()
|
|
233
|
-
.positions.filter((pos) =>
|
|
234
|
-
marketIndex ? pos.marketIndex === marketIndex : true
|
|
235
|
-
)
|
|
236
|
-
.reduce((pnl, marketPosition) => {
|
|
237
|
-
const market = this.clearingHouse.getMarketAccount(
|
|
238
|
-
marketPosition.marketIndex
|
|
239
|
-
);
|
|
240
|
-
return pnl.add(
|
|
241
|
-
calculatePositionPNL(
|
|
242
|
-
market,
|
|
243
|
-
marketPosition,
|
|
244
|
-
withFunding,
|
|
245
|
-
this.getOracleDataForMarket(market.marketIndex)
|
|
246
|
-
)
|
|
247
|
-
);
|
|
248
|
-
}, ZERO);
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
/**
|
|
252
|
-
* calculates unrealized position price pnl
|
|
253
|
-
* @returns : Precision QUOTE_PRECISION
|
|
254
|
-
*/
|
|
255
|
-
public getUnsettledPNL(marketIndex?: BN): BN {
|
|
256
|
-
return this.getUserAccount()
|
|
257
|
-
.positions.filter((pos) =>
|
|
258
|
-
marketIndex ? pos.marketIndex === marketIndex : true
|
|
259
|
-
)
|
|
260
|
-
.reduce((pnl, marketPosition) => {
|
|
261
|
-
return pnl.add(marketPosition.unsettledPnl);
|
|
262
|
-
}, ZERO);
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
/**
|
|
266
|
-
* calculates unrealized funding payment pnl
|
|
267
|
-
* @returns : Precision QUOTE_PRECISION
|
|
268
|
-
*/
|
|
269
|
-
public getUnrealizedFundingPNL(marketIndex?: BN): BN {
|
|
270
|
-
return this.getUserAccount()
|
|
271
|
-
.positions.filter((pos) =>
|
|
272
|
-
marketIndex ? pos.marketIndex === marketIndex : true
|
|
273
|
-
)
|
|
274
|
-
.reduce((pnl, marketPosition) => {
|
|
275
|
-
const market = this.clearingHouse.getMarketAccount(
|
|
276
|
-
marketPosition.marketIndex
|
|
277
|
-
);
|
|
278
|
-
return pnl.add(calculatePositionFundingPNL(market, marketPosition));
|
|
279
|
-
}, ZERO);
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
public getTotalLiability(): BN {
|
|
283
|
-
return this.getUserAccount().bankBalances.reduce(
|
|
284
|
-
(totalAssetValue, bankBalance) => {
|
|
285
|
-
if (
|
|
286
|
-
bankBalance.balance.eq(ZERO) ||
|
|
287
|
-
isVariant(bankBalance.balanceType, 'deposit')
|
|
288
|
-
) {
|
|
289
|
-
return totalAssetValue;
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
// Todo this needs to account for whether it's based on initial or maintenance requirements
|
|
293
|
-
const bankAccount: BankAccount = this.clearingHouse.getBankAccount(
|
|
294
|
-
bankBalance.bankIndex
|
|
295
|
-
);
|
|
296
|
-
|
|
297
|
-
const tokenAmount = getTokenAmount(
|
|
298
|
-
bankBalance.balance,
|
|
299
|
-
bankAccount,
|
|
300
|
-
bankBalance.balanceType
|
|
301
|
-
);
|
|
302
|
-
return totalAssetValue.add(
|
|
303
|
-
tokenAmount
|
|
304
|
-
.mul(this.getOracleDataForBank(bankAccount.bankIndex).price)
|
|
305
|
-
.mul(bankAccount.initialLiabilityWeight)
|
|
306
|
-
.div(BANK_WEIGHT_PRECISION)
|
|
307
|
-
.div(MARK_PRICE_PRECISION)
|
|
308
|
-
);
|
|
309
|
-
},
|
|
310
|
-
ZERO
|
|
311
|
-
);
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
public getCollateralValue(bankIndex?: BN): BN {
|
|
315
|
-
return this.getUserAccount().bankBalances.reduce(
|
|
316
|
-
(totalAssetValue, bankBalance) => {
|
|
317
|
-
if (
|
|
318
|
-
bankBalance.balance.eq(ZERO) ||
|
|
319
|
-
(bankIndex !== undefined && !bankBalance.bankIndex.eq(bankIndex))
|
|
320
|
-
) {
|
|
321
|
-
return totalAssetValue;
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
// Todo this needs to account for whether it's based on initial or maintenance requirements
|
|
325
|
-
const bankAccount: BankAccount = this.clearingHouse.getBankAccount(
|
|
326
|
-
bankBalance.bankIndex
|
|
327
|
-
);
|
|
328
|
-
|
|
329
|
-
let tokenAmount = getTokenAmount(
|
|
330
|
-
bankBalance.balance,
|
|
331
|
-
bankAccount,
|
|
332
|
-
bankBalance.balanceType
|
|
333
|
-
);
|
|
334
|
-
|
|
335
|
-
if (isVariant(bankBalance.balanceType, 'borrow')) {
|
|
336
|
-
tokenAmount = tokenAmount.mul(new BN(-1));
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
return totalAssetValue.add(
|
|
340
|
-
tokenAmount
|
|
341
|
-
.mul(this.getOracleDataForBank(bankAccount.bankIndex).price)
|
|
342
|
-
.div(MARK_PRICE_PRECISION)
|
|
343
|
-
);
|
|
344
|
-
},
|
|
345
|
-
ZERO
|
|
346
|
-
);
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
/**
|
|
350
|
-
* calculates TotalCollateral: collateral + unrealized pnl
|
|
351
|
-
* TODO: rename to total equity (for perpetuals swaps)
|
|
352
|
-
* @returns : Precision QUOTE_PRECISION
|
|
353
|
-
*/
|
|
354
|
-
public getTotalCollateral(): BN {
|
|
355
|
-
return this.getUserAccount()
|
|
356
|
-
.bankBalances.reduce((totalAssetValue, bankBalance) => {
|
|
357
|
-
if (
|
|
358
|
-
bankBalance.balance.eq(ZERO) ||
|
|
359
|
-
isVariant(bankBalance.balanceType, 'borrow')
|
|
360
|
-
) {
|
|
361
|
-
return totalAssetValue;
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
// Todo this needs to account for whether it's based on initial or maintenance requirements
|
|
365
|
-
const bankAccount: BankAccount = this.clearingHouse.getBankAccount(
|
|
366
|
-
bankBalance.bankIndex
|
|
367
|
-
);
|
|
368
|
-
|
|
369
|
-
const tokenAmount = getTokenAmount(
|
|
370
|
-
bankBalance.balance,
|
|
371
|
-
bankAccount,
|
|
372
|
-
bankBalance.balanceType
|
|
373
|
-
);
|
|
374
|
-
return totalAssetValue.add(
|
|
375
|
-
tokenAmount
|
|
376
|
-
.mul(this.getOracleDataForBank(bankAccount.bankIndex).price)
|
|
377
|
-
.mul(bankAccount.initialAssetWeight)
|
|
378
|
-
.div(BANK_WEIGHT_PRECISION)
|
|
379
|
-
.div(MARK_PRICE_PRECISION)
|
|
380
|
-
);
|
|
381
|
-
}, ZERO)
|
|
382
|
-
.add(this.getUnrealizedPNL(true))
|
|
383
|
-
.add(this.getUnsettledPNL());
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
/**
|
|
387
|
-
* calculates sum of position value across all positions
|
|
388
|
-
* @returns : Precision QUOTE_PRECISION
|
|
389
|
-
*/
|
|
390
|
-
getTotalPositionValue(): BN {
|
|
391
|
-
return this.getUserAccount().positions.reduce(
|
|
392
|
-
(positionValue, marketPosition) => {
|
|
393
|
-
const market = this.clearingHouse.getMarketAccount(
|
|
394
|
-
marketPosition.marketIndex
|
|
395
|
-
);
|
|
396
|
-
return positionValue.add(
|
|
397
|
-
calculateBaseAssetValue(
|
|
398
|
-
market,
|
|
399
|
-
marketPosition,
|
|
400
|
-
this.getOracleDataForMarket(market.marketIndex)
|
|
401
|
-
)
|
|
402
|
-
);
|
|
403
|
-
},
|
|
404
|
-
ZERO
|
|
405
|
-
);
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
/**
|
|
409
|
-
* calculates position value from closing 100%
|
|
410
|
-
* @returns : Precision QUOTE_PRECISION
|
|
411
|
-
*/
|
|
412
|
-
public getPositionValue(
|
|
413
|
-
marketIndex: BN,
|
|
414
|
-
oraclePriceData: OraclePriceData
|
|
415
|
-
): BN {
|
|
416
|
-
const userPosition =
|
|
417
|
-
this.getUserPosition(marketIndex) || this.getEmptyPosition(marketIndex);
|
|
418
|
-
const market = this.clearingHouse.getMarketAccount(
|
|
419
|
-
userPosition.marketIndex
|
|
420
|
-
);
|
|
421
|
-
return calculateBaseAssetValue(market, userPosition, oraclePriceData);
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
public getPositionSide(
|
|
425
|
-
currentPosition: Pick<UserPosition, 'baseAssetAmount'>
|
|
426
|
-
): PositionDirection | undefined {
|
|
427
|
-
if (currentPosition.baseAssetAmount.gt(ZERO)) {
|
|
428
|
-
return PositionDirection.LONG;
|
|
429
|
-
} else if (currentPosition.baseAssetAmount.lt(ZERO)) {
|
|
430
|
-
return PositionDirection.SHORT;
|
|
431
|
-
} else {
|
|
432
|
-
return undefined;
|
|
433
|
-
}
|
|
434
|
-
}
|
|
435
|
-
|
|
436
|
-
/**
|
|
437
|
-
* calculates average exit price for closing 100% of position
|
|
438
|
-
* @returns : Precision MARK_PRICE_PRECISION
|
|
439
|
-
*/
|
|
440
|
-
public getPositionEstimatedExitPriceAndPnl(
|
|
441
|
-
position: UserPosition,
|
|
442
|
-
amountToClose?: BN
|
|
443
|
-
): [BN, BN] {
|
|
444
|
-
const market = this.clearingHouse.getMarketAccount(position.marketIndex);
|
|
445
|
-
|
|
446
|
-
const entryPrice = calculateEntryPrice(position);
|
|
447
|
-
|
|
448
|
-
const oraclePriceData = this.getOracleDataForMarket(position.marketIndex);
|
|
449
|
-
|
|
450
|
-
if (amountToClose) {
|
|
451
|
-
if (amountToClose.eq(ZERO)) {
|
|
452
|
-
return [calculateMarkPrice(market, oraclePriceData), ZERO];
|
|
453
|
-
}
|
|
454
|
-
position = {
|
|
455
|
-
baseAssetAmount: amountToClose,
|
|
456
|
-
lastCumulativeFundingRate: position.lastCumulativeFundingRate,
|
|
457
|
-
marketIndex: position.marketIndex,
|
|
458
|
-
quoteAssetAmount: position.quoteAssetAmount,
|
|
459
|
-
} as UserPosition;
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
const baseAssetValue = calculateBaseAssetValue(
|
|
463
|
-
market,
|
|
464
|
-
position,
|
|
465
|
-
oraclePriceData
|
|
466
|
-
);
|
|
467
|
-
if (position.baseAssetAmount.eq(ZERO)) {
|
|
468
|
-
return [ZERO, ZERO];
|
|
469
|
-
}
|
|
470
|
-
|
|
471
|
-
const exitPrice = baseAssetValue
|
|
472
|
-
.mul(AMM_TO_QUOTE_PRECISION_RATIO)
|
|
473
|
-
.mul(MARK_PRICE_PRECISION)
|
|
474
|
-
.div(position.baseAssetAmount.abs());
|
|
475
|
-
|
|
476
|
-
const pnlPerBase = exitPrice.sub(entryPrice);
|
|
477
|
-
const pnl = pnlPerBase
|
|
478
|
-
.mul(position.baseAssetAmount)
|
|
479
|
-
.div(MARK_PRICE_PRECISION)
|
|
480
|
-
.div(AMM_TO_QUOTE_PRECISION_RATIO);
|
|
481
|
-
|
|
482
|
-
return [exitPrice, pnl];
|
|
483
|
-
}
|
|
484
|
-
|
|
485
|
-
/**
|
|
486
|
-
* calculates current user leverage across all positions
|
|
487
|
-
* @returns : Precision TEN_THOUSAND
|
|
488
|
-
*/
|
|
489
|
-
public getLeverage(): BN {
|
|
490
|
-
const totalCollateral = this.getTotalCollateral();
|
|
491
|
-
const totalPositionValue = this.getTotalPositionValue();
|
|
492
|
-
if (totalPositionValue.eq(ZERO) && totalCollateral.eq(ZERO)) {
|
|
493
|
-
return ZERO;
|
|
494
|
-
}
|
|
495
|
-
return totalPositionValue.mul(TEN_THOUSAND).div(totalCollateral);
|
|
496
|
-
}
|
|
497
|
-
|
|
498
|
-
/**
|
|
499
|
-
* calculates max allowable leverage exceeding hitting requirement category
|
|
500
|
-
* @params category {Initial, Partial, Maintenance}
|
|
501
|
-
* @returns : Precision TEN_THOUSAND
|
|
502
|
-
*/
|
|
503
|
-
public getMaxLeverage(
|
|
504
|
-
marketIndex: BN | number,
|
|
505
|
-
category: MarginCategory = 'Initial'
|
|
506
|
-
): BN {
|
|
507
|
-
const market = this.clearingHouse.getMarketAccount(marketIndex);
|
|
508
|
-
let marginRatioCategory: number;
|
|
509
|
-
|
|
510
|
-
switch (category) {
|
|
511
|
-
case 'Initial':
|
|
512
|
-
marginRatioCategory = market.marginRatioInitial;
|
|
513
|
-
break;
|
|
514
|
-
case 'Maintenance':
|
|
515
|
-
marginRatioCategory = market.marginRatioMaintenance;
|
|
516
|
-
break;
|
|
517
|
-
default:
|
|
518
|
-
marginRatioCategory = market.marginRatioInitial;
|
|
519
|
-
break;
|
|
520
|
-
}
|
|
521
|
-
const maxLeverage = TEN_THOUSAND.mul(TEN_THOUSAND).div(
|
|
522
|
-
new BN(marginRatioCategory)
|
|
523
|
-
);
|
|
524
|
-
return maxLeverage;
|
|
525
|
-
}
|
|
526
|
-
|
|
527
|
-
/**
|
|
528
|
-
* calculates margin ratio: total collateral / |total position value|
|
|
529
|
-
* @returns : Precision TEN_THOUSAND
|
|
530
|
-
*/
|
|
531
|
-
public getMarginRatio(): BN {
|
|
532
|
-
const totalPositionValue = this.getTotalPositionValue();
|
|
533
|
-
|
|
534
|
-
if (totalPositionValue.eq(ZERO)) {
|
|
535
|
-
return BN_MAX;
|
|
536
|
-
}
|
|
537
|
-
|
|
538
|
-
return this.getTotalCollateral().mul(TEN_THOUSAND).div(totalPositionValue);
|
|
539
|
-
}
|
|
540
|
-
|
|
541
|
-
public canBeLiquidated(): [boolean, BN] {
|
|
542
|
-
const totalCollateral = this.getTotalCollateral();
|
|
543
|
-
const partialMaintenanceRequirement =
|
|
544
|
-
this.getMaintenanceMarginRequirement();
|
|
545
|
-
const marginRatio = this.getMarginRatio();
|
|
546
|
-
const canLiquidate = totalCollateral.lt(partialMaintenanceRequirement);
|
|
547
|
-
return [canLiquidate, marginRatio];
|
|
548
|
-
}
|
|
549
|
-
|
|
550
|
-
/**
|
|
551
|
-
* Checks if any user position cumulative funding differs from respective market cumulative funding
|
|
552
|
-
* @returns
|
|
553
|
-
*/
|
|
554
|
-
public needsToSettleFundingPayment(): boolean {
|
|
555
|
-
for (const userPosition of this.getUserAccount().positions) {
|
|
556
|
-
if (userPosition.baseAssetAmount.eq(ZERO)) {
|
|
557
|
-
continue;
|
|
558
|
-
}
|
|
559
|
-
|
|
560
|
-
const market = this.clearingHouse.getMarketAccount(
|
|
561
|
-
userPosition.marketIndex
|
|
562
|
-
);
|
|
563
|
-
if (
|
|
564
|
-
market.amm.cumulativeFundingRateLong.eq(
|
|
565
|
-
userPosition.lastCumulativeFundingRate
|
|
566
|
-
) ||
|
|
567
|
-
market.amm.cumulativeFundingRateShort.eq(
|
|
568
|
-
userPosition.lastCumulativeFundingRate
|
|
569
|
-
)
|
|
570
|
-
) {
|
|
571
|
-
continue;
|
|
572
|
-
}
|
|
573
|
-
|
|
574
|
-
return true;
|
|
575
|
-
}
|
|
576
|
-
return false;
|
|
577
|
-
}
|
|
578
|
-
|
|
579
|
-
/**
|
|
580
|
-
* Calculate the liquidation price of a position, with optional parameter to calculate the liquidation price after a trade
|
|
581
|
-
* @param marketPosition
|
|
582
|
-
* @param positionBaseSizeChange // change in position size to calculate liquidation price for : Precision 10^13
|
|
583
|
-
* @param partial
|
|
584
|
-
* @returns Precision : MARK_PRICE_PRECISION
|
|
585
|
-
*/
|
|
586
|
-
public liquidationPrice(
|
|
587
|
-
marketPosition: Pick<UserPosition, 'marketIndex'>,
|
|
588
|
-
positionBaseSizeChange: BN = ZERO
|
|
589
|
-
): BN {
|
|
590
|
-
// solves formula for example canBeLiquidated below
|
|
591
|
-
|
|
592
|
-
/* example: assume BTC price is $40k (examine 10% up/down)
|
|
593
|
-
|
|
594
|
-
if 10k deposit and levered 10x short BTC => BTC up $400 means:
|
|
595
|
-
1. higher base_asset_value (+$4k)
|
|
596
|
-
2. lower collateral (-$4k)
|
|
597
|
-
3. (10k - 4k)/(100k + 4k) => 6k/104k => .0576
|
|
598
|
-
|
|
599
|
-
for 10x long, BTC down $400:
|
|
600
|
-
3. (10k - 4k) / (100k - 4k) = 6k/96k => .0625 */
|
|
601
|
-
|
|
602
|
-
const totalCollateral = this.getTotalCollateral();
|
|
603
|
-
|
|
604
|
-
// calculate the total position value ignoring any value from the target market of the trade
|
|
605
|
-
const totalPositionValueExcludingTargetMarket =
|
|
606
|
-
this.getTotalPositionValueExcludingMarket(marketPosition.marketIndex);
|
|
607
|
-
|
|
608
|
-
const currentMarketPosition =
|
|
609
|
-
this.getUserPosition(marketPosition.marketIndex) ||
|
|
610
|
-
this.getEmptyPosition(marketPosition.marketIndex);
|
|
611
|
-
|
|
612
|
-
const currentMarketPositionBaseSize = currentMarketPosition.baseAssetAmount;
|
|
613
|
-
|
|
614
|
-
const proposedBaseAssetAmount = currentMarketPositionBaseSize.add(
|
|
615
|
-
positionBaseSizeChange
|
|
616
|
-
);
|
|
617
|
-
|
|
618
|
-
// calculate position for current market after trade
|
|
619
|
-
const proposedMarketPosition: UserPosition = {
|
|
620
|
-
marketIndex: marketPosition.marketIndex,
|
|
621
|
-
baseAssetAmount: proposedBaseAssetAmount,
|
|
622
|
-
lastCumulativeFundingRate:
|
|
623
|
-
currentMarketPosition.lastCumulativeFundingRate,
|
|
624
|
-
quoteAssetAmount: new BN(0),
|
|
625
|
-
quoteEntryAmount: new BN(0),
|
|
626
|
-
openOrders: new BN(0),
|
|
627
|
-
unsettledPnl: new BN(0),
|
|
628
|
-
openBids: new BN(0),
|
|
629
|
-
openAsks: new BN(0),
|
|
630
|
-
};
|
|
631
|
-
|
|
632
|
-
if (proposedBaseAssetAmount.eq(ZERO)) return new BN(-1);
|
|
633
|
-
|
|
634
|
-
const market = this.clearingHouse.getMarketAccount(
|
|
635
|
-
proposedMarketPosition.marketIndex
|
|
636
|
-
);
|
|
637
|
-
|
|
638
|
-
const proposedMarketPositionValue = calculateBaseAssetValue(
|
|
639
|
-
market,
|
|
640
|
-
proposedMarketPosition,
|
|
641
|
-
this.getOracleDataForMarket(market.marketIndex)
|
|
642
|
-
);
|
|
643
|
-
|
|
644
|
-
// total position value after trade
|
|
645
|
-
const totalPositionValueAfterTrade =
|
|
646
|
-
totalPositionValueExcludingTargetMarket.add(proposedMarketPositionValue);
|
|
647
|
-
|
|
648
|
-
const marginRequirementExcludingTargetMarket =
|
|
649
|
-
this.getUserAccount().positions.reduce(
|
|
650
|
-
(totalMarginRequirement, position) => {
|
|
651
|
-
if (!position.marketIndex.eq(marketPosition.marketIndex)) {
|
|
652
|
-
const market = this.clearingHouse.getMarketAccount(
|
|
653
|
-
position.marketIndex
|
|
654
|
-
);
|
|
655
|
-
const positionValue = calculateBaseAssetValue(
|
|
656
|
-
market,
|
|
657
|
-
position,
|
|
658
|
-
this.getOracleDataForMarket(market.marketIndex)
|
|
659
|
-
);
|
|
660
|
-
const marketMarginRequirement = positionValue
|
|
661
|
-
.mul(new BN(market.marginRatioMaintenance))
|
|
662
|
-
.div(MARGIN_PRECISION);
|
|
663
|
-
totalMarginRequirement = totalMarginRequirement.add(
|
|
664
|
-
marketMarginRequirement
|
|
665
|
-
);
|
|
666
|
-
}
|
|
667
|
-
return totalMarginRequirement;
|
|
668
|
-
},
|
|
669
|
-
ZERO
|
|
670
|
-
);
|
|
671
|
-
|
|
672
|
-
const freeCollateralExcludingTargetMarket = totalCollateral.sub(
|
|
673
|
-
marginRequirementExcludingTargetMarket
|
|
674
|
-
);
|
|
675
|
-
|
|
676
|
-
// if the position value after the trade is less than free collateral, there is no liq price
|
|
677
|
-
if (
|
|
678
|
-
totalPositionValueAfterTrade.lte(freeCollateralExcludingTargetMarket) &&
|
|
679
|
-
proposedMarketPosition.baseAssetAmount.abs().gt(ZERO)
|
|
680
|
-
) {
|
|
681
|
-
return new BN(-1);
|
|
682
|
-
}
|
|
683
|
-
|
|
684
|
-
const marginRequirementAfterTrade =
|
|
685
|
-
marginRequirementExcludingTargetMarket.add(
|
|
686
|
-
proposedMarketPositionValue
|
|
687
|
-
.mul(new BN(market.marginRatioMaintenance))
|
|
688
|
-
.div(MARGIN_PRECISION)
|
|
689
|
-
);
|
|
690
|
-
const freeCollateralAfterTrade = totalCollateral.sub(
|
|
691
|
-
marginRequirementAfterTrade
|
|
692
|
-
);
|
|
693
|
-
|
|
694
|
-
const marketMaxLeverage = this.getMaxLeverage(
|
|
695
|
-
proposedMarketPosition.marketIndex,
|
|
696
|
-
'Maintenance'
|
|
697
|
-
);
|
|
698
|
-
|
|
699
|
-
let priceDelta;
|
|
700
|
-
if (proposedBaseAssetAmount.lt(ZERO)) {
|
|
701
|
-
priceDelta = freeCollateralAfterTrade
|
|
702
|
-
.mul(marketMaxLeverage) // precision is TEN_THOUSAND
|
|
703
|
-
.div(marketMaxLeverage.add(TEN_THOUSAND))
|
|
704
|
-
.mul(PRICE_TO_QUOTE_PRECISION)
|
|
705
|
-
.mul(AMM_RESERVE_PRECISION)
|
|
706
|
-
.div(proposedBaseAssetAmount);
|
|
707
|
-
} else {
|
|
708
|
-
priceDelta = freeCollateralAfterTrade
|
|
709
|
-
.mul(marketMaxLeverage) // precision is TEN_THOUSAND
|
|
710
|
-
.div(marketMaxLeverage.sub(TEN_THOUSAND))
|
|
711
|
-
.mul(PRICE_TO_QUOTE_PRECISION)
|
|
712
|
-
.mul(AMM_RESERVE_PRECISION)
|
|
713
|
-
.div(proposedBaseAssetAmount);
|
|
714
|
-
}
|
|
715
|
-
|
|
716
|
-
let markPriceAfterTrade;
|
|
717
|
-
if (positionBaseSizeChange.eq(ZERO)) {
|
|
718
|
-
markPriceAfterTrade = calculateMarkPrice(
|
|
719
|
-
this.clearingHouse.getMarketAccount(marketPosition.marketIndex),
|
|
720
|
-
this.getOracleDataForMarket(marketPosition.marketIndex)
|
|
721
|
-
);
|
|
722
|
-
} else {
|
|
723
|
-
const direction = positionBaseSizeChange.gt(ZERO)
|
|
724
|
-
? PositionDirection.LONG
|
|
725
|
-
: PositionDirection.SHORT;
|
|
726
|
-
markPriceAfterTrade = calculateTradeSlippage(
|
|
727
|
-
direction,
|
|
728
|
-
positionBaseSizeChange.abs(),
|
|
729
|
-
this.clearingHouse.getMarketAccount(marketPosition.marketIndex),
|
|
730
|
-
'base',
|
|
731
|
-
this.getOracleDataForMarket(marketPosition.marketIndex)
|
|
732
|
-
)[3]; // newPrice after swap
|
|
733
|
-
}
|
|
734
|
-
|
|
735
|
-
if (priceDelta.gt(markPriceAfterTrade)) {
|
|
736
|
-
return new BN(-1);
|
|
737
|
-
}
|
|
738
|
-
|
|
739
|
-
return markPriceAfterTrade.sub(priceDelta);
|
|
740
|
-
}
|
|
741
|
-
|
|
742
|
-
/**
|
|
743
|
-
* Calculates the estimated liquidation price for a position after closing a quote amount of the position.
|
|
744
|
-
* @param positionMarketIndex
|
|
745
|
-
* @param closeQuoteAmount
|
|
746
|
-
* @returns : Precision MARK_PRICE_PRECISION
|
|
747
|
-
*/
|
|
748
|
-
public liquidationPriceAfterClose(
|
|
749
|
-
positionMarketIndex: BN,
|
|
750
|
-
closeQuoteAmount: BN
|
|
751
|
-
): BN {
|
|
752
|
-
const currentPosition =
|
|
753
|
-
this.getUserPosition(positionMarketIndex) ||
|
|
754
|
-
this.getEmptyPosition(positionMarketIndex);
|
|
755
|
-
|
|
756
|
-
const closeBaseAmount = currentPosition.baseAssetAmount
|
|
757
|
-
.mul(closeQuoteAmount)
|
|
758
|
-
.div(currentPosition.quoteAssetAmount)
|
|
759
|
-
.add(
|
|
760
|
-
currentPosition.baseAssetAmount
|
|
761
|
-
.mul(closeQuoteAmount)
|
|
762
|
-
.mod(currentPosition.quoteAssetAmount)
|
|
763
|
-
)
|
|
764
|
-
.neg();
|
|
765
|
-
|
|
766
|
-
return this.liquidationPrice(
|
|
767
|
-
{
|
|
768
|
-
marketIndex: positionMarketIndex,
|
|
769
|
-
},
|
|
770
|
-
closeBaseAmount
|
|
771
|
-
);
|
|
772
|
-
}
|
|
773
|
-
|
|
774
|
-
/**
|
|
775
|
-
* Get the maximum trade size for a given market, taking into account the user's current leverage, positions, collateral, etc.
|
|
776
|
-
*
|
|
777
|
-
* To Calculate Max Quote Available:
|
|
778
|
-
*
|
|
779
|
-
* Case 1: SameSide
|
|
780
|
-
* => Remaining quote to get to maxLeverage
|
|
781
|
-
*
|
|
782
|
-
* Case 2: NOT SameSide && currentLeverage <= maxLeverage
|
|
783
|
-
* => Current opposite position x2 + remaining to get to maxLeverage
|
|
784
|
-
*
|
|
785
|
-
* Case 3: NOT SameSide && currentLeverage > maxLeverage && otherPositions - currentPosition > maxLeverage
|
|
786
|
-
* => strictly reduce current position size
|
|
787
|
-
*
|
|
788
|
-
* Case 4: NOT SameSide && currentLeverage > maxLeverage && otherPositions - currentPosition < maxLeverage
|
|
789
|
-
* => current position + remaining to get to maxLeverage
|
|
790
|
-
*
|
|
791
|
-
* @param targetMarketIndex
|
|
792
|
-
* @param tradeSide
|
|
793
|
-
* @returns tradeSizeAllowed : Precision QUOTE_PRECISION
|
|
794
|
-
*/
|
|
795
|
-
public getMaxTradeSizeUSDC(
|
|
796
|
-
targetMarketIndex: BN,
|
|
797
|
-
tradeSide: PositionDirection
|
|
798
|
-
): BN {
|
|
799
|
-
const currentPosition =
|
|
800
|
-
this.getUserPosition(targetMarketIndex) ||
|
|
801
|
-
this.getEmptyPosition(targetMarketIndex);
|
|
802
|
-
|
|
803
|
-
const targetSide = isVariant(tradeSide, 'short') ? 'short' : 'long';
|
|
804
|
-
|
|
805
|
-
const currentPositionSide = currentPosition?.baseAssetAmount.isNeg()
|
|
806
|
-
? 'short'
|
|
807
|
-
: 'long';
|
|
808
|
-
|
|
809
|
-
const targetingSameSide = !currentPosition
|
|
810
|
-
? true
|
|
811
|
-
: targetSide === currentPositionSide;
|
|
812
|
-
|
|
813
|
-
const oracleData = this.getOracleDataForMarket(targetMarketIndex);
|
|
814
|
-
|
|
815
|
-
// 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.
|
|
816
|
-
const oppositeSizeValueUSDC = targetingSameSide
|
|
817
|
-
? ZERO
|
|
818
|
-
: this.getPositionValue(targetMarketIndex, oracleData);
|
|
819
|
-
|
|
820
|
-
let maxPositionSize = this.getBuyingPower(targetMarketIndex);
|
|
821
|
-
if (maxPositionSize.gte(ZERO)) {
|
|
822
|
-
if (oppositeSizeValueUSDC.eq(ZERO)) {
|
|
823
|
-
// case 1 : Regular trade where current total position less than max, and no opposite position to account for
|
|
824
|
-
// do nothing
|
|
825
|
-
} else {
|
|
826
|
-
// case 2 : trade where current total position less than max, but need to account for flipping the current position over to the other side
|
|
827
|
-
maxPositionSize = maxPositionSize.add(
|
|
828
|
-
oppositeSizeValueUSDC.mul(new BN(2))
|
|
829
|
-
);
|
|
830
|
-
}
|
|
831
|
-
} else {
|
|
832
|
-
// current leverage is greater than max leverage - can only reduce position size
|
|
833
|
-
|
|
834
|
-
if (!targetingSameSide) {
|
|
835
|
-
const market = this.clearingHouse.getMarketAccount(targetMarketIndex);
|
|
836
|
-
const marketPositionValue = this.getPositionValue(
|
|
837
|
-
targetMarketIndex,
|
|
838
|
-
oracleData
|
|
839
|
-
);
|
|
840
|
-
const totalCollateral = this.getTotalCollateral();
|
|
841
|
-
const marginRequirement = this.getInitialMarginRequirement();
|
|
842
|
-
const marginFreedByClosing = marketPositionValue
|
|
843
|
-
.mul(new BN(market.marginRatioInitial))
|
|
844
|
-
.div(MARGIN_PRECISION);
|
|
845
|
-
const marginRequirementAfterClosing =
|
|
846
|
-
marginRequirement.sub(marginFreedByClosing);
|
|
847
|
-
|
|
848
|
-
if (marginRequirementAfterClosing.gt(totalCollateral)) {
|
|
849
|
-
maxPositionSize = marketPositionValue;
|
|
850
|
-
} else {
|
|
851
|
-
const freeCollateralAfterClose = totalCollateral.sub(
|
|
852
|
-
marginRequirementAfterClosing
|
|
853
|
-
);
|
|
854
|
-
const buyingPowerAfterClose = freeCollateralAfterClose
|
|
855
|
-
.mul(this.getMaxLeverage(targetMarketIndex))
|
|
856
|
-
.div(TEN_THOUSAND);
|
|
857
|
-
maxPositionSize = marketPositionValue.add(buyingPowerAfterClose);
|
|
858
|
-
}
|
|
859
|
-
} else {
|
|
860
|
-
// do nothing if targetting same side
|
|
861
|
-
}
|
|
862
|
-
}
|
|
863
|
-
|
|
864
|
-
// subtract oneMillionth of maxPositionSize
|
|
865
|
-
// => to avoid rounding errors when taking max leverage
|
|
866
|
-
const oneMilli = maxPositionSize.div(QUOTE_PRECISION);
|
|
867
|
-
return maxPositionSize.sub(oneMilli);
|
|
868
|
-
}
|
|
869
|
-
|
|
870
|
-
// TODO - should this take the price impact of the trade into account for strict accuracy?
|
|
871
|
-
|
|
872
|
-
/**
|
|
873
|
-
* Returns the leverage ratio for the account after adding (or subtracting) the given quote size to the given position
|
|
874
|
-
* @param targetMarketIndex
|
|
875
|
-
* @param positionMarketIndex
|
|
876
|
-
* @param tradeQuoteAmount
|
|
877
|
-
* @returns leverageRatio : Precision TEN_THOUSAND
|
|
878
|
-
*/
|
|
879
|
-
public accountLeverageRatioAfterTrade(
|
|
880
|
-
targetMarketIndex: BN,
|
|
881
|
-
tradeQuoteAmount: BN,
|
|
882
|
-
tradeSide: PositionDirection
|
|
883
|
-
): BN {
|
|
884
|
-
const currentPosition =
|
|
885
|
-
this.getUserPosition(targetMarketIndex) ||
|
|
886
|
-
this.getEmptyPosition(targetMarketIndex);
|
|
887
|
-
|
|
888
|
-
const oracleData = this.getOracleDataForMarket(targetMarketIndex);
|
|
889
|
-
|
|
890
|
-
let currentPositionQuoteAmount = this.getPositionValue(
|
|
891
|
-
targetMarketIndex,
|
|
892
|
-
oracleData
|
|
893
|
-
);
|
|
894
|
-
|
|
895
|
-
const currentSide =
|
|
896
|
-
currentPosition && currentPosition.baseAssetAmount.isNeg()
|
|
897
|
-
? PositionDirection.SHORT
|
|
898
|
-
: PositionDirection.LONG;
|
|
899
|
-
|
|
900
|
-
if (currentSide === PositionDirection.SHORT)
|
|
901
|
-
currentPositionQuoteAmount = currentPositionQuoteAmount.neg();
|
|
902
|
-
|
|
903
|
-
if (tradeSide === PositionDirection.SHORT)
|
|
904
|
-
tradeQuoteAmount = tradeQuoteAmount.neg();
|
|
905
|
-
|
|
906
|
-
const currentMarketPositionAfterTrade = currentPositionQuoteAmount
|
|
907
|
-
.add(tradeQuoteAmount)
|
|
908
|
-
.abs();
|
|
909
|
-
|
|
910
|
-
const totalPositionAfterTradeExcludingTargetMarket =
|
|
911
|
-
this.getTotalPositionValueExcludingMarket(targetMarketIndex);
|
|
912
|
-
|
|
913
|
-
const totalCollateral = this.getTotalCollateral();
|
|
914
|
-
if (totalCollateral.gt(ZERO)) {
|
|
915
|
-
const newLeverage = currentMarketPositionAfterTrade
|
|
916
|
-
.add(totalPositionAfterTradeExcludingTargetMarket)
|
|
917
|
-
.abs()
|
|
918
|
-
.mul(TEN_THOUSAND)
|
|
919
|
-
.div(totalCollateral);
|
|
920
|
-
return newLeverage;
|
|
921
|
-
} else {
|
|
922
|
-
return new BN(0);
|
|
923
|
-
}
|
|
924
|
-
}
|
|
925
|
-
|
|
926
|
-
/**
|
|
927
|
-
* Calculates how much fee will be taken for a given sized trade
|
|
928
|
-
* @param quoteAmount
|
|
929
|
-
* @returns feeForQuote : Precision QUOTE_PRECISION
|
|
930
|
-
*/
|
|
931
|
-
public calculateFeeForQuoteAmount(quoteAmount: BN): BN {
|
|
932
|
-
const feeStructure = this.clearingHouse.getStateAccount().feeStructure;
|
|
933
|
-
|
|
934
|
-
return quoteAmount
|
|
935
|
-
.mul(feeStructure.feeNumerator)
|
|
936
|
-
.div(feeStructure.feeDenominator);
|
|
937
|
-
}
|
|
938
|
-
|
|
939
|
-
/**
|
|
940
|
-
* Get the total position value, excluding any position coming from the given target market
|
|
941
|
-
* @param marketToIgnore
|
|
942
|
-
* @returns positionValue : Precision QUOTE_PRECISION
|
|
943
|
-
*/
|
|
944
|
-
private getTotalPositionValueExcludingMarket(marketToIgnore: BN): BN {
|
|
945
|
-
const currentMarketPosition =
|
|
946
|
-
this.getUserPosition(marketToIgnore) ||
|
|
947
|
-
this.getEmptyPosition(marketToIgnore);
|
|
948
|
-
|
|
949
|
-
const oracleData = this.getOracleDataForMarket(marketToIgnore);
|
|
950
|
-
|
|
951
|
-
let currentMarketPositionValueUSDC = ZERO;
|
|
952
|
-
if (currentMarketPosition) {
|
|
953
|
-
currentMarketPositionValueUSDC = this.getPositionValue(
|
|
954
|
-
marketToIgnore,
|
|
955
|
-
oracleData
|
|
956
|
-
);
|
|
957
|
-
}
|
|
958
|
-
|
|
959
|
-
return this.getTotalPositionValue().sub(currentMarketPositionValueUSDC);
|
|
960
|
-
}
|
|
961
|
-
|
|
962
|
-
private getOracleDataForMarket(marketIndex: BN): OraclePriceData {
|
|
963
|
-
const oracleKey =
|
|
964
|
-
this.clearingHouse.getMarketAccount(marketIndex).amm.oracle;
|
|
965
|
-
const oracleData =
|
|
966
|
-
this.clearingHouse.getOraclePriceDataAndSlot(oracleKey).data;
|
|
967
|
-
|
|
968
|
-
return oracleData;
|
|
969
|
-
}
|
|
970
|
-
private getOracleDataForBank(bankIndex: BN): OraclePriceData {
|
|
971
|
-
const oracleKey = this.clearingHouse.getBankAccount(bankIndex).oracle;
|
|
972
|
-
|
|
973
|
-
const oracleData =
|
|
974
|
-
this.clearingHouse.getOraclePriceDataAndSlot(oracleKey).data;
|
|
975
|
-
|
|
976
|
-
return oracleData;
|
|
977
|
-
}
|
|
978
|
-
}
|