@drift-labs/sdk 0.2.0-temp.2 → 2.0.1

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.
Files changed (278) hide show
  1. package/README.md +27 -29
  2. package/lib/accounts/bulkAccountLoader.d.ts +2 -0
  3. package/lib/accounts/bulkAccountLoader.js +36 -29
  4. package/lib/accounts/bulkUserStatsSubscription.d.ts +7 -0
  5. package/lib/accounts/bulkUserStatsSubscription.js +21 -0
  6. package/lib/accounts/bulkUserSubscription.d.ts +2 -2
  7. package/lib/accounts/bulkUserSubscription.js +0 -1
  8. package/lib/accounts/fetch.d.ts +2 -1
  9. package/lib/accounts/fetch.js +9 -1
  10. package/lib/accounts/{pollingClearingHouseAccountSubscriber.d.ts → pollingDriftClientAccountSubscriber.d.ts} +20 -25
  11. package/lib/accounts/{pollingClearingHouseAccountSubscriber.js → pollingDriftClientAccountSubscriber.js} +45 -49
  12. package/lib/accounts/{pollingOracleSubscriber.d.ts → pollingOracleAccountSubscriber.d.ts} +2 -2
  13. package/lib/accounts/{pollingOracleSubscriber.js → pollingOracleAccountSubscriber.js} +3 -3
  14. package/lib/accounts/pollingUserAccountSubscriber.d.ts +1 -1
  15. package/lib/accounts/pollingUserAccountSubscriber.js +5 -11
  16. package/lib/accounts/pollingUserStatsAccountSubscriber.d.ts +27 -0
  17. package/lib/accounts/pollingUserStatsAccountSubscriber.js +107 -0
  18. package/lib/accounts/types.d.ts +26 -15
  19. package/lib/accounts/webSocketDriftClientAccountSubscriber.d.ts +49 -0
  20. package/lib/accounts/{webSocketClearingHouseAccountSubscriber.js → webSocketDriftClientAccountSubscriber.js} +47 -45
  21. package/lib/accounts/webSocketUserStatsAccountSubsriber.d.ts +20 -0
  22. package/lib/accounts/webSocketUserStatsAccountSubsriber.js +47 -0
  23. package/lib/addresses/marketAddresses.d.ts +1 -3
  24. package/lib/addresses/marketAddresses.js +1 -1
  25. package/lib/addresses/pda.d.ts +15 -9
  26. package/lib/addresses/pda.js +73 -35
  27. package/lib/adminClient.d.ts +65 -0
  28. package/lib/adminClient.js +637 -0
  29. package/lib/config.d.ts +10 -10
  30. package/lib/config.js +26 -22
  31. package/lib/constants/numericConstants.d.ts +29 -12
  32. package/lib/constants/numericConstants.js +40 -21
  33. package/lib/constants/perpMarkets.d.ts +18 -0
  34. package/lib/constants/{markets.js → perpMarkets.js} +20 -9
  35. package/lib/constants/spotMarkets.d.ts +19 -0
  36. package/lib/constants/spotMarkets.js +63 -0
  37. package/lib/dlob/DLOB.d.ts +82 -0
  38. package/lib/dlob/DLOB.js +694 -0
  39. package/lib/dlob/DLOBNode.d.ts +54 -0
  40. package/lib/dlob/DLOBNode.js +77 -0
  41. package/lib/dlob/NodeList.d.ts +27 -0
  42. package/lib/dlob/NodeList.js +144 -0
  43. package/lib/driftClient.d.ts +234 -0
  44. package/lib/driftClient.js +2108 -0
  45. package/lib/{clearingHouseConfig.d.ts → driftClientConfig.d.ts} +9 -9
  46. package/lib/{clearingHouseConfig.js → driftClientConfig.js} +0 -0
  47. package/lib/events/eventList.d.ts +0 -1
  48. package/lib/events/eventList.js +0 -7
  49. package/lib/events/eventSubscriber.d.ts +5 -2
  50. package/lib/events/eventSubscriber.js +25 -11
  51. package/lib/events/fetchLogs.d.ts +13 -2
  52. package/lib/events/fetchLogs.js +45 -14
  53. package/lib/events/pollingLogProvider.d.ts +2 -1
  54. package/lib/events/pollingLogProvider.js +7 -3
  55. package/lib/events/sort.js +8 -11
  56. package/lib/events/txEventCache.d.ts +0 -2
  57. package/lib/events/txEventCache.js +0 -14
  58. package/lib/events/types.d.ts +11 -3
  59. package/lib/events/types.js +8 -0
  60. package/lib/events/webSocketLogProvider.js +1 -1
  61. package/lib/examples/makeTradeExample.js +30 -18
  62. package/lib/factory/bigNum.d.ts +8 -4
  63. package/lib/factory/bigNum.js +109 -19
  64. package/lib/idl/drift.json +8392 -0
  65. package/lib/index.d.ts +29 -12
  66. package/lib/index.js +29 -12
  67. package/lib/math/amm.d.ts +9 -6
  68. package/lib/math/amm.js +91 -38
  69. package/lib/math/conversion.js +1 -1
  70. package/lib/math/exchangeStatus.d.ts +4 -0
  71. package/lib/math/exchangeStatus.js +18 -0
  72. package/lib/math/funding.d.ts +6 -6
  73. package/lib/math/funding.js +23 -21
  74. package/lib/math/insurance.d.ts +4 -0
  75. package/lib/math/insurance.js +27 -0
  76. package/lib/math/margin.d.ts +11 -0
  77. package/lib/math/margin.js +82 -0
  78. package/lib/math/market.d.ts +14 -9
  79. package/lib/math/market.js +70 -10
  80. package/lib/math/oracles.d.ts +4 -0
  81. package/lib/math/oracles.js +36 -8
  82. package/lib/math/orders.d.ts +16 -6
  83. package/lib/math/orders.js +97 -17
  84. package/lib/math/position.d.ts +27 -13
  85. package/lib/math/position.js +91 -37
  86. package/lib/math/repeg.js +17 -8
  87. package/lib/math/spotBalance.d.ts +22 -0
  88. package/lib/math/spotBalance.js +192 -0
  89. package/lib/math/spotMarket.d.ts +4 -0
  90. package/lib/math/spotMarket.js +8 -0
  91. package/lib/math/spotPosition.d.ts +6 -0
  92. package/lib/math/spotPosition.js +23 -0
  93. package/lib/math/trade.d.ts +10 -10
  94. package/lib/math/trade.js +27 -31
  95. package/lib/oracles/pythClient.js +1 -1
  96. package/lib/oracles/quoteAssetOracleClient.js +1 -1
  97. package/lib/oracles/switchboardClient.js +1 -1
  98. package/lib/orderParams.d.ts +4 -4
  99. package/lib/orderParams.js +12 -4
  100. package/lib/serum/serumFulfillmentConfigMap.d.ts +10 -0
  101. package/lib/serum/serumFulfillmentConfigMap.js +17 -0
  102. package/lib/serum/serumSubscriber.d.ts +27 -0
  103. package/lib/serum/serumSubscriber.js +56 -0
  104. package/lib/serum/types.d.ts +11 -0
  105. package/{src/oracles → lib/serum}/types.js +0 -0
  106. package/lib/tokenFaucet.d.ts +1 -0
  107. package/lib/tokenFaucet.js +23 -12
  108. package/lib/tx/retryTxSender.d.ts +1 -1
  109. package/lib/tx/retryTxSender.js +13 -4
  110. package/lib/tx/types.d.ts +1 -1
  111. package/lib/types.d.ts +631 -222
  112. package/lib/types.js +137 -24
  113. package/lib/user.d.ts +228 -0
  114. package/lib/user.js +959 -0
  115. package/lib/userConfig.d.ts +14 -0
  116. package/lib/{clearingHouseUserConfig.js → userConfig.js} +0 -0
  117. package/lib/userMap/userMap.d.ts +41 -0
  118. package/lib/userMap/userMap.js +85 -0
  119. package/lib/userMap/userStatsMap.d.ts +19 -0
  120. package/lib/userMap/userStatsMap.js +68 -0
  121. package/lib/userName.d.ts +1 -0
  122. package/lib/userName.js +3 -2
  123. package/lib/userStats.d.ts +18 -0
  124. package/lib/userStats.js +49 -0
  125. package/lib/userStatsConfig.d.ts +14 -0
  126. package/{src/clearingHouseConfig.js → lib/userStatsConfig.js} +0 -0
  127. package/lib/util/getTokenAddress.d.ts +2 -0
  128. package/lib/util/getTokenAddress.js +9 -0
  129. package/package.json +12 -5
  130. package/src/accounts/bulkAccountLoader.ts +44 -34
  131. package/src/accounts/bulkUserStatsSubscription.ts +33 -0
  132. package/src/accounts/bulkUserSubscription.ts +2 -3
  133. package/src/accounts/fetch.ts +27 -2
  134. package/src/accounts/{pollingClearingHouseAccountSubscriber.ts → pollingDriftClientAccountSubscriber.ts} +65 -75
  135. package/src/accounts/{pollingOracleSubscriber.ts → pollingOracleAccountSubscriber.ts} +2 -2
  136. package/src/accounts/pollingUserAccountSubscriber.ts +5 -12
  137. package/src/accounts/pollingUserStatsAccountSubscriber.ts +166 -0
  138. package/src/accounts/types.ts +35 -15
  139. package/src/accounts/{webSocketClearingHouseAccountSubscriber.ts → webSocketDriftClientAccountSubscriber.ts} +78 -73
  140. package/src/accounts/webSocketUserStatsAccountSubsriber.ts +80 -0
  141. package/src/addresses/marketAddresses.ts +3 -4
  142. package/src/addresses/pda.ts +105 -33
  143. package/src/adminClient.ts +1207 -0
  144. package/src/config.ts +41 -34
  145. package/src/constants/numericConstants.ts +59 -26
  146. package/src/constants/{markets.ts → perpMarkets.ts} +22 -11
  147. package/src/constants/spotMarkets.ts +83 -0
  148. package/src/dlob/DLOB.ts +1120 -0
  149. package/src/dlob/DLOBNode.ts +155 -0
  150. package/src/dlob/NodeList.ts +195 -0
  151. package/src/driftClient.ts +3594 -0
  152. package/src/{clearingHouseConfig.ts → driftClientConfig.ts} +9 -8
  153. package/src/events/eventList.ts +1 -8
  154. package/src/events/eventSubscriber.ts +36 -14
  155. package/src/events/fetchLogs.ts +60 -15
  156. package/src/events/pollingLogProvider.ts +11 -3
  157. package/src/events/sort.ts +11 -15
  158. package/src/events/txEventCache.ts +0 -16
  159. package/src/events/types.ts +27 -2
  160. package/src/events/webSocketLogProvider.ts +1 -1
  161. package/src/examples/makeTradeExample.js +152 -75
  162. package/src/examples/makeTradeExample.ts +44 -28
  163. package/src/factory/bigNum.ts +150 -22
  164. package/src/idl/drift.json +8392 -0
  165. package/src/idl/pyth.json +98 -2
  166. package/src/index.ts +29 -12
  167. package/src/math/amm.ts +161 -48
  168. package/src/math/conversion.ts +2 -2
  169. package/src/math/exchangeStatus.ts +31 -0
  170. package/src/math/funding.ts +41 -31
  171. package/src/math/insurance.ts +35 -0
  172. package/src/math/margin.ts +133 -0
  173. package/src/math/market.ts +143 -14
  174. package/src/math/oracles.ts +63 -9
  175. package/src/math/orders.ts +168 -26
  176. package/src/math/position.ts +133 -59
  177. package/src/math/repeg.ts +19 -9
  178. package/src/math/spotBalance.ts +319 -0
  179. package/src/math/spotMarket.ts +9 -0
  180. package/src/math/spotPosition.ts +47 -0
  181. package/src/math/trade.ts +33 -37
  182. package/src/oracles/pythClient.ts +2 -2
  183. package/src/oracles/quoteAssetOracleClient.ts +2 -2
  184. package/src/oracles/switchboardClient.ts +2 -2
  185. package/src/orderParams.ts +16 -8
  186. package/src/serum/serumFulfillmentConfigMap.ts +26 -0
  187. package/src/serum/serumSubscriber.ts +99 -0
  188. package/src/serum/types.ts +13 -0
  189. package/src/tokenFaucet.ts +38 -15
  190. package/src/tx/retryTxSender.ts +16 -5
  191. package/src/tx/types.ts +2 -1
  192. package/src/types.ts +594 -195
  193. package/src/user.ts +1599 -0
  194. package/src/{clearingHouseUserConfig.ts → userConfig.ts} +5 -5
  195. package/src/userMap/userMap.ts +124 -0
  196. package/src/userMap/userStatsMap.ts +108 -0
  197. package/src/userName.ts +2 -1
  198. package/src/userStats.ts +75 -0
  199. package/src/userStatsConfig.ts +18 -0
  200. package/src/util/getTokenAddress.ts +18 -0
  201. package/tests/bn/test.ts +46 -11
  202. package/tests/dlob/helpers.ts +619 -0
  203. package/tests/dlob/test.ts +4586 -0
  204. package/lib/accounts/webSocketClearingHouseAccountSubscriber.d.ts +0 -49
  205. package/lib/admin.d.ts +0 -44
  206. package/lib/admin.js +0 -438
  207. package/lib/clearingHouse.d.ts +0 -146
  208. package/lib/clearingHouse.js +0 -1154
  209. package/lib/clearingHouseUser.d.ts +0 -187
  210. package/lib/clearingHouseUser.js +0 -634
  211. package/lib/clearingHouseUserConfig.d.ts +0 -14
  212. package/lib/constants/banks.d.ts +0 -16
  213. package/lib/constants/banks.js +0 -41
  214. package/lib/constants/markets.d.ts +0 -19
  215. package/lib/idl/clearing_house.json +0 -4464
  216. package/lib/math/bankBalance.d.ts +0 -9
  217. package/lib/math/bankBalance.js +0 -75
  218. package/lib/math/state.d.ts +0 -8
  219. package/lib/math/state.js +0 -15
  220. package/lib/orders.d.ts +0 -8
  221. package/lib/orders.js +0 -134
  222. package/src/accounts/bulkAccountLoader.js +0 -197
  223. package/src/accounts/bulkUserSubscription.js +0 -33
  224. package/src/accounts/pollingClearingHouseAccountSubscriber.js +0 -311
  225. package/src/accounts/pollingOracleSubscriber.js +0 -93
  226. package/src/accounts/pollingTokenAccountSubscriber.js +0 -90
  227. package/src/accounts/pollingUserAccountSubscriber.js +0 -132
  228. package/src/accounts/types.js +0 -10
  229. package/src/accounts/utils.js +0 -7
  230. package/src/accounts/webSocketAccountSubscriber.js +0 -93
  231. package/src/accounts/webSocketClearingHouseAccountSubscriber.js +0 -233
  232. package/src/accounts/webSocketUserAccountSubscriber.js +0 -62
  233. package/src/addresses/marketAddresses.js +0 -26
  234. package/src/admin.js +0 -517
  235. package/src/admin.ts +0 -730
  236. package/src/clearingHouse.ts +0 -1828
  237. package/src/clearingHouseUser.ts +0 -978
  238. package/src/clearingHouseUserConfig.js +0 -2
  239. package/src/config.js +0 -67
  240. package/src/constants/banks.js +0 -42
  241. package/src/constants/banks.ts +0 -50
  242. package/src/constants/markets.js +0 -42
  243. package/src/constants/numericConstants.js +0 -41
  244. package/src/events/eventSubscriber.js +0 -139
  245. package/src/events/fetchLogs.js +0 -50
  246. package/src/events/pollingLogProvider.js +0 -64
  247. package/src/events/sort.js +0 -44
  248. package/src/events/txEventCache.js +0 -71
  249. package/src/events/types.js +0 -20
  250. package/src/events/webSocketLogProvider.js +0 -41
  251. package/src/factory/bigNum.js +0 -390
  252. package/src/factory/oracleClient.js +0 -20
  253. package/src/idl/clearing_house.json +0 -4464
  254. package/src/index.js +0 -69
  255. package/src/math/amm.js +0 -369
  256. package/src/math/auction.js +0 -42
  257. package/src/math/bankBalance.ts +0 -112
  258. package/src/math/conversion.js +0 -11
  259. package/src/math/funding.js +0 -248
  260. package/src/math/oracles.js +0 -26
  261. package/src/math/repeg.js +0 -128
  262. package/src/math/state.js +0 -15
  263. package/src/math/state.ts +0 -14
  264. package/src/math/trade.js +0 -253
  265. package/src/math/utils.js +0 -26
  266. package/src/math/utils.js.map +0 -1
  267. package/src/mockUSDCFaucet.js +0 -280
  268. package/src/oracles/oracleClientCache.js +0 -19
  269. package/src/oracles/pythClient.js +0 -46
  270. package/src/oracles/quoteAssetOracleClient.js +0 -32
  271. package/src/oracles/switchboardClient.js +0 -69
  272. package/src/orderParams.js +0 -20
  273. package/src/orders.ts +0 -245
  274. package/src/slot/SlotSubscriber.js +0 -39
  275. package/src/tokenFaucet.js +0 -189
  276. package/src/types.js +0 -125
  277. package/src/userName.js +0 -20
  278. package/src/wallet.js +0 -35
@@ -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
- }