@drift-labs/sdk 0.2.0-master.4 → 0.2.0-master.40

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 (234) hide show
  1. package/README.md +27 -27
  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/pollingUserStatsAccountSubscriber.d.ts +27 -0
  15. package/lib/accounts/pollingUserStatsAccountSubscriber.js +113 -0
  16. package/lib/accounts/types.d.ts +26 -15
  17. package/lib/accounts/webSocketDriftClientAccountSubscriber.d.ts +49 -0
  18. package/lib/accounts/{webSocketClearingHouseAccountSubscriber.js → webSocketDriftClientAccountSubscriber.js} +47 -45
  19. package/lib/accounts/webSocketUserStatsAccountSubsriber.d.ts +20 -0
  20. package/lib/accounts/webSocketUserStatsAccountSubsriber.js +47 -0
  21. package/lib/addresses/marketAddresses.d.ts +1 -3
  22. package/lib/addresses/marketAddresses.js +1 -1
  23. package/lib/addresses/pda.d.ts +15 -9
  24. package/lib/addresses/pda.js +73 -35
  25. package/lib/adminClient.d.ts +65 -0
  26. package/lib/adminClient.js +637 -0
  27. package/lib/config.d.ts +9 -9
  28. package/lib/config.js +25 -21
  29. package/lib/constants/numericConstants.d.ts +30 -12
  30. package/lib/constants/numericConstants.js +41 -21
  31. package/lib/constants/perpMarkets.d.ts +18 -0
  32. package/lib/constants/{markets.js → perpMarkets.js} +7 -7
  33. package/lib/constants/spotMarkets.d.ts +19 -0
  34. package/lib/constants/spotMarkets.js +53 -0
  35. package/lib/dlob/DLOB.d.ts +82 -0
  36. package/lib/dlob/DLOB.js +696 -0
  37. package/lib/dlob/DLOBNode.d.ts +54 -0
  38. package/lib/dlob/DLOBNode.js +77 -0
  39. package/lib/dlob/NodeList.d.ts +27 -0
  40. package/lib/dlob/NodeList.js +144 -0
  41. package/lib/driftClient.d.ts +233 -0
  42. package/lib/driftClient.js +2096 -0
  43. package/lib/{clearingHouseConfig.d.ts → driftClientConfig.d.ts} +9 -9
  44. package/lib/{clearingHouseConfig.js → driftClientConfig.js} +0 -0
  45. package/lib/events/eventList.js +3 -0
  46. package/lib/events/eventSubscriber.d.ts +5 -2
  47. package/lib/events/eventSubscriber.js +25 -11
  48. package/lib/events/fetchLogs.d.ts +13 -2
  49. package/lib/events/fetchLogs.js +40 -12
  50. package/lib/events/pollingLogProvider.d.ts +2 -1
  51. package/lib/events/pollingLogProvider.js +7 -3
  52. package/lib/events/sort.js +8 -11
  53. package/lib/events/types.d.ts +9 -3
  54. package/lib/events/types.js +6 -0
  55. package/lib/events/webSocketLogProvider.js +1 -1
  56. package/lib/examples/makeTradeExample.js +30 -18
  57. package/lib/factory/bigNum.d.ts +8 -4
  58. package/lib/factory/bigNum.js +109 -19
  59. package/lib/idl/drift.json +8250 -0
  60. package/lib/idl/{mock_usdc_faucet.json → token_faucet.json} +46 -23
  61. package/lib/index.d.ts +30 -13
  62. package/lib/index.js +30 -13
  63. package/lib/math/amm.d.ts +9 -6
  64. package/lib/math/amm.js +91 -38
  65. package/lib/math/conversion.js +1 -1
  66. package/lib/math/exchangeStatus.d.ts +4 -0
  67. package/lib/math/exchangeStatus.js +18 -0
  68. package/lib/math/funding.d.ts +6 -6
  69. package/lib/math/funding.js +23 -21
  70. package/lib/math/insurance.d.ts +4 -0
  71. package/lib/math/insurance.js +27 -0
  72. package/lib/math/margin.d.ts +11 -0
  73. package/lib/math/margin.js +82 -0
  74. package/lib/math/market.d.ts +14 -9
  75. package/lib/math/market.js +70 -10
  76. package/lib/math/oracles.d.ts +4 -0
  77. package/lib/math/oracles.js +36 -8
  78. package/lib/math/orders.d.ts +14 -6
  79. package/lib/math/orders.js +90 -17
  80. package/lib/math/position.d.ts +27 -13
  81. package/lib/math/position.js +92 -36
  82. package/lib/math/repeg.js +17 -8
  83. package/lib/math/spotBalance.d.ts +22 -0
  84. package/lib/math/spotBalance.js +192 -0
  85. package/lib/math/spotMarket.d.ts +4 -0
  86. package/lib/math/spotMarket.js +8 -0
  87. package/lib/math/spotPosition.d.ts +6 -0
  88. package/lib/math/spotPosition.js +23 -0
  89. package/lib/math/trade.d.ts +10 -10
  90. package/lib/math/trade.js +27 -31
  91. package/lib/oracles/pythClient.js +1 -1
  92. package/lib/oracles/quoteAssetOracleClient.js +1 -1
  93. package/lib/oracles/switchboardClient.js +1 -1
  94. package/lib/orderParams.d.ts +4 -4
  95. package/lib/orderParams.js +12 -4
  96. package/lib/serum/serumFulfillmentConfigMap.d.ts +10 -0
  97. package/lib/serum/serumFulfillmentConfigMap.js +17 -0
  98. package/lib/serum/serumSubscriber.d.ts +27 -0
  99. package/lib/serum/serumSubscriber.js +56 -0
  100. package/lib/serum/types.d.ts +11 -0
  101. package/lib/{clearingHouseUserConfig.js → serum/types.js} +0 -0
  102. package/lib/slot/SlotSubscriber.d.ts +7 -0
  103. package/lib/slot/SlotSubscriber.js +3 -0
  104. package/lib/{mockUSDCFaucet.d.ts → tokenFaucet.d.ts} +8 -5
  105. package/lib/{mockUSDCFaucet.js → tokenFaucet.js} +63 -51
  106. package/lib/tx/retryTxSender.d.ts +1 -1
  107. package/lib/tx/retryTxSender.js +13 -4
  108. package/lib/tx/types.d.ts +1 -1
  109. package/lib/tx/utils.js +1 -1
  110. package/lib/types.d.ts +589 -196
  111. package/lib/types.js +108 -17
  112. package/lib/user.d.ts +226 -0
  113. package/lib/user.js +949 -0
  114. package/lib/userConfig.d.ts +14 -0
  115. package/lib/userConfig.js +2 -0
  116. package/lib/userMap/userMap.d.ts +41 -0
  117. package/lib/userMap/userMap.js +85 -0
  118. package/lib/userMap/userStatsMap.d.ts +19 -0
  119. package/lib/userMap/userStatsMap.js +68 -0
  120. package/lib/userName.d.ts +1 -0
  121. package/lib/userName.js +3 -2
  122. package/lib/userStats.d.ts +18 -0
  123. package/lib/userStats.js +49 -0
  124. package/lib/userStatsConfig.d.ts +14 -0
  125. package/lib/userStatsConfig.js +2 -0
  126. package/lib/util/computeUnits.js +1 -1
  127. package/lib/util/getTokenAddress.d.ts +2 -0
  128. package/lib/util/getTokenAddress.js +9 -0
  129. package/package.json +10 -3
  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/pollingUserStatsAccountSubscriber.ts +172 -0
  137. package/src/accounts/types.ts +35 -15
  138. package/src/accounts/{webSocketClearingHouseAccountSubscriber.ts → webSocketDriftClientAccountSubscriber.ts} +78 -73
  139. package/src/accounts/webSocketUserStatsAccountSubsriber.ts +80 -0
  140. package/src/addresses/marketAddresses.ts +3 -4
  141. package/src/addresses/pda.ts +105 -33
  142. package/src/adminClient.ts +1207 -0
  143. package/src/config.ts +37 -31
  144. package/src/constants/numericConstants.ts +58 -24
  145. package/src/constants/{markets.ts → perpMarkets.ts} +10 -10
  146. package/src/constants/spotMarkets.ts +73 -0
  147. package/src/dlob/DLOB.ts +1123 -0
  148. package/src/dlob/DLOBNode.ts +155 -0
  149. package/src/dlob/NodeList.ts +195 -0
  150. package/src/driftClient.ts +3564 -0
  151. package/src/{clearingHouseConfig.ts → driftClientConfig.ts} +9 -8
  152. package/src/events/eventList.ts +3 -0
  153. package/src/events/eventSubscriber.ts +36 -14
  154. package/src/events/fetchLogs.ts +55 -13
  155. package/src/events/pollingLogProvider.ts +11 -3
  156. package/src/events/sort.ts +11 -15
  157. package/src/events/types.ts +21 -2
  158. package/src/events/webSocketLogProvider.ts +1 -1
  159. package/src/examples/makeTradeExample.ts +44 -28
  160. package/src/factory/bigNum.ts +150 -22
  161. package/src/idl/drift.json +8250 -0
  162. package/src/idl/pyth.json +98 -2
  163. package/src/idl/{mock_usdc_faucet.json → token_faucet.json} +46 -23
  164. package/src/index.ts +30 -13
  165. package/src/math/amm.ts +161 -48
  166. package/src/math/conversion.ts +2 -2
  167. package/src/math/exchangeStatus.ts +31 -0
  168. package/src/math/funding.ts +41 -31
  169. package/src/math/insurance.ts +35 -0
  170. package/src/math/margin.ts +133 -0
  171. package/src/math/market.ts +143 -14
  172. package/src/math/oracles.ts +63 -9
  173. package/src/math/orders.ts +163 -26
  174. package/src/math/position.ts +136 -58
  175. package/src/math/repeg.ts +19 -9
  176. package/src/math/spotBalance.ts +319 -0
  177. package/src/math/spotMarket.ts +9 -0
  178. package/src/math/spotPosition.ts +47 -0
  179. package/src/math/trade.ts +33 -37
  180. package/src/oracles/pythClient.ts +2 -2
  181. package/src/oracles/quoteAssetOracleClient.ts +2 -2
  182. package/src/oracles/switchboardClient.ts +2 -2
  183. package/src/orderParams.ts +16 -8
  184. package/src/serum/serumFulfillmentConfigMap.ts +26 -0
  185. package/src/serum/serumSubscriber.ts +99 -0
  186. package/src/serum/types.ts +13 -0
  187. package/src/slot/SlotSubscriber.ts +11 -1
  188. package/src/{mockUSDCFaucet.ts → tokenFaucet.ts} +82 -70
  189. package/src/tx/retryTxSender.ts +16 -5
  190. package/src/tx/types.ts +2 -1
  191. package/src/tx/utils.ts +1 -1
  192. package/src/types.ts +572 -178
  193. package/src/user.ts +1582 -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/computeUnits.ts +1 -1
  201. package/src/util/getTokenAddress.ts +18 -0
  202. package/tests/bn/test.ts +46 -11
  203. package/tests/dlob/helpers.ts +611 -0
  204. package/tests/dlob/test.ts +4588 -0
  205. package/lib/accounts/webSocketClearingHouseAccountSubscriber.d.ts +0 -49
  206. package/lib/admin.d.ts +0 -44
  207. package/lib/admin.js +0 -433
  208. package/lib/clearingHouse.d.ts +0 -133
  209. package/lib/clearingHouse.js +0 -931
  210. package/lib/clearingHouseUser.d.ts +0 -187
  211. package/lib/clearingHouseUser.js +0 -643
  212. package/lib/clearingHouseUserConfig.d.ts +0 -14
  213. package/lib/constants/banks.d.ts +0 -16
  214. package/lib/constants/banks.js +0 -34
  215. package/lib/constants/markets.d.ts +0 -19
  216. package/lib/idl/clearing_house.json +0 -3998
  217. package/lib/math/bankBalance.d.ts +0 -9
  218. package/lib/math/bankBalance.js +0 -75
  219. package/lib/math/state.d.ts +0 -8
  220. package/lib/math/state.js +0 -15
  221. package/lib/orders.d.ts +0 -8
  222. package/lib/orders.js +0 -134
  223. package/src/admin.ts +0 -722
  224. package/src/clearingHouse.ts +0 -1451
  225. package/src/clearingHouseUser.ts +0 -989
  226. package/src/constants/banks.ts +0 -43
  227. package/src/idl/clearing_house.json +0 -3998
  228. package/src/math/bankBalance.ts +0 -112
  229. package/src/math/state.ts +0 -14
  230. package/src/math/utils.js +0 -27
  231. package/src/math/utils.js.map +0 -1
  232. package/src/orders.ts +0 -244
  233. package/src/util/computeUnits.js +0 -17
  234. package/src/util/computeUnits.js.map +0 -1
@@ -0,0 +1,133 @@
1
+ import { squareRootBN } from './utils';
2
+ import {
3
+ SPOT_MARKET_WEIGHT_PRECISION,
4
+ SPOT_MARKET_IMF_PRECISION,
5
+ ZERO,
6
+ BID_ASK_SPREAD_PRECISION,
7
+ AMM_RESERVE_PRECISION,
8
+ } from '../constants/numericConstants';
9
+ import { BN } from '@project-serum/anchor';
10
+ import { OraclePriceData } from '../oracles/types';
11
+ import { PerpMarketAccount, PerpPosition } from '..';
12
+ import { isVariant } from '../types';
13
+ import { assert } from '../assert/assert';
14
+
15
+ export function calculateSizePremiumLiabilityWeight(
16
+ size: BN, // AMM_RESERVE_PRECISION
17
+ imfFactor: BN,
18
+ liabilityWeight: BN,
19
+ precision: BN
20
+ ): BN {
21
+ if (imfFactor.eq(ZERO)) {
22
+ return liabilityWeight;
23
+ }
24
+
25
+ const sizeSqrt = squareRootBN(size.mul(new BN(10)).add(new BN(1))); //1e9 -> 1e10 -> 1e5
26
+
27
+ const denom0 = BN.max(new BN(1), SPOT_MARKET_IMF_PRECISION.div(imfFactor));
28
+ assert(denom0.gt(ZERO));
29
+ const liabilityWeightNumerator = liabilityWeight.sub(
30
+ liabilityWeight.div(
31
+ BN.max(new BN(1), SPOT_MARKET_IMF_PRECISION.div(imfFactor))
32
+ )
33
+ );
34
+
35
+ const denom = new BN(100_000).mul(SPOT_MARKET_IMF_PRECISION).div(precision);
36
+ assert(denom.gt(ZERO));
37
+
38
+ const sizePremiumLiabilityWeight = liabilityWeightNumerator.add(
39
+ sizeSqrt // 1e5
40
+ .mul(imfFactor)
41
+ .div(denom) // 1e5
42
+ );
43
+
44
+ const maxLiabilityWeight = BN.max(
45
+ liabilityWeight,
46
+ sizePremiumLiabilityWeight
47
+ );
48
+ return maxLiabilityWeight;
49
+ }
50
+
51
+ export function calculateSizeDiscountAssetWeight(
52
+ size: BN, // AMM_RESERVE_PRECISION
53
+ imfFactor: BN,
54
+ assetWeight: BN
55
+ ): BN {
56
+ if (imfFactor.eq(ZERO)) {
57
+ return assetWeight;
58
+ }
59
+
60
+ const sizeSqrt = squareRootBN(size.mul(new BN(10)).add(new BN(1))); //1e9 -> 1e10 -> 1e5
61
+ const imfNumerator = SPOT_MARKET_IMF_PRECISION.add(
62
+ SPOT_MARKET_IMF_PRECISION.div(new BN(10))
63
+ );
64
+
65
+ const sizeDiscountAssetWeight = imfNumerator
66
+ .mul(SPOT_MARKET_WEIGHT_PRECISION)
67
+ .div(
68
+ SPOT_MARKET_IMF_PRECISION.add(
69
+ sizeSqrt // 1e5
70
+ .mul(imfFactor)
71
+ .div(new BN(100_000)) // 1e5
72
+ )
73
+ );
74
+
75
+ const minAssetWeight = BN.min(assetWeight, sizeDiscountAssetWeight);
76
+
77
+ return minAssetWeight;
78
+ }
79
+
80
+ export function calculateOraclePriceForPerpMargin(
81
+ perpPosition: PerpPosition,
82
+ market: PerpMarketAccount,
83
+ oraclePriceData: OraclePriceData
84
+ ): BN {
85
+ const oraclePriceOffset = BN.min(
86
+ new BN(market.amm.maxSpread)
87
+ .mul(oraclePriceData.price)
88
+ .div(BID_ASK_SPREAD_PRECISION),
89
+ oraclePriceData.confidence.add(
90
+ new BN(market.amm.baseSpread)
91
+ .mul(oraclePriceData.price)
92
+ .div(BID_ASK_SPREAD_PRECISION)
93
+ )
94
+ );
95
+
96
+ let marginPrice: BN;
97
+ if (perpPosition.baseAssetAmount.gt(ZERO)) {
98
+ marginPrice = oraclePriceData.price.sub(oraclePriceOffset);
99
+ } else {
100
+ marginPrice = oraclePriceData.price.add(oraclePriceOffset);
101
+ }
102
+
103
+ return marginPrice;
104
+ }
105
+
106
+ export function calculateBaseAssetValueWithOracle(
107
+ market: PerpMarketAccount,
108
+ perpPosition: PerpPosition,
109
+ oraclePriceData: OraclePriceData
110
+ ): BN {
111
+ let price = oraclePriceData.price;
112
+ if (isVariant(market.status, 'settlement')) {
113
+ price = market.expiryPrice;
114
+ }
115
+
116
+ return perpPosition.baseAssetAmount
117
+ .abs()
118
+ .mul(price)
119
+ .div(AMM_RESERVE_PRECISION);
120
+ }
121
+
122
+ export function calculateWorstCaseBaseAssetAmount(
123
+ perpPosition: PerpPosition
124
+ ): BN {
125
+ const allBids = perpPosition.baseAssetAmount.add(perpPosition.openBids);
126
+ const allAsks = perpPosition.baseAssetAmount.add(perpPosition.openAsks);
127
+
128
+ if (allBids.abs().gt(allAsks.abs())) {
129
+ return allBids;
130
+ } else {
131
+ return allAsks;
132
+ }
133
+ }
@@ -1,5 +1,11 @@
1
1
  import { BN } from '@project-serum/anchor';
2
- import { MarketAccount, PositionDirection } from '../types';
2
+ import {
3
+ PerpMarketAccount,
4
+ PositionDirection,
5
+ MarginCategory,
6
+ SpotMarketAccount,
7
+ SpotBalanceType,
8
+ } from '../types';
3
9
  import {
4
10
  calculateAmmReservesAfterSwap,
5
11
  calculatePrice,
@@ -7,16 +13,27 @@ import {
7
13
  getSwapDirection,
8
14
  calculateUpdatedAMM,
9
15
  } from './amm';
16
+ import {
17
+ calculateSizeDiscountAssetWeight,
18
+ calculateSizePremiumLiabilityWeight,
19
+ } from './margin';
10
20
  import { OraclePriceData } from '../oracles/types';
21
+ import {
22
+ BASE_PRECISION,
23
+ MARGIN_PRECISION,
24
+ PRICE_TO_QUOTE_PRECISION,
25
+ ZERO,
26
+ } from '../constants/numericConstants';
27
+ import { getTokenAmount } from './spotBalance';
11
28
 
12
29
  /**
13
30
  * Calculates market mark price
14
31
  *
15
32
  * @param market
16
- * @return markPrice : Precision MARK_PRICE_PRECISION
33
+ * @return markPrice : Precision PRICE_PRECISION
17
34
  */
18
- export function calculateMarkPrice(
19
- market: MarketAccount,
35
+ export function calculateReservePrice(
36
+ market: PerpMarketAccount,
20
37
  oraclePriceData: OraclePriceData
21
38
  ): BN {
22
39
  const newAmm = calculateUpdatedAMM(market.amm, oraclePriceData);
@@ -31,10 +48,10 @@ export function calculateMarkPrice(
31
48
  * Calculates market bid price
32
49
  *
33
50
  * @param market
34
- * @return bidPrice : Precision MARK_PRICE_PRECISION
51
+ * @return bidPrice : Precision PRICE_PRECISION
35
52
  */
36
53
  export function calculateBidPrice(
37
- market: MarketAccount,
54
+ market: PerpMarketAccount,
38
55
  oraclePriceData: OraclePriceData
39
56
  ): BN {
40
57
  const { baseAssetReserve, quoteAssetReserve, newPeg } =
@@ -51,10 +68,10 @@ export function calculateBidPrice(
51
68
  * Calculates market ask price
52
69
  *
53
70
  * @param market
54
- * @return bidPrice : Precision MARK_PRICE_PRECISION
71
+ * @return askPrice : Precision PRICE_PRECISION
55
72
  */
56
73
  export function calculateAskPrice(
57
- market: MarketAccount,
74
+ market: PerpMarketAccount,
58
75
  oraclePriceData: OraclePriceData
59
76
  ): BN {
60
77
  const { baseAssetReserve, quoteAssetReserve, newPeg } =
@@ -70,8 +87,8 @@ export function calculateAskPrice(
70
87
  export function calculateNewMarketAfterTrade(
71
88
  baseAssetAmount: BN,
72
89
  direction: PositionDirection,
73
- market: MarketAccount
74
- ): MarketAccount {
90
+ market: PerpMarketAccount
91
+ ): PerpMarketAccount {
75
92
  const [newQuoteAssetReserve, newBaseAssetReserve] =
76
93
  calculateAmmReservesAfterSwap(
77
94
  market.amm,
@@ -89,12 +106,12 @@ export function calculateNewMarketAfterTrade(
89
106
  return newMarket;
90
107
  }
91
108
 
92
- export function calculateMarkOracleSpread(
93
- market: MarketAccount,
109
+ export function calculateOracleReserveSpread(
110
+ market: PerpMarketAccount,
94
111
  oraclePriceData: OraclePriceData
95
112
  ): BN {
96
- const markPrice = calculateMarkPrice(market, oraclePriceData);
97
- return calculateOracleSpread(markPrice, oraclePriceData);
113
+ const reservePrice = calculateReservePrice(market, oraclePriceData);
114
+ return calculateOracleSpread(reservePrice, oraclePriceData);
98
115
  }
99
116
 
100
117
  export function calculateOracleSpread(
@@ -103,3 +120,115 @@ export function calculateOracleSpread(
103
120
  ): BN {
104
121
  return price.sub(oraclePriceData.price);
105
122
  }
123
+
124
+ export function calculateMarketMarginRatio(
125
+ market: PerpMarketAccount,
126
+ size: BN,
127
+ marginCategory: MarginCategory
128
+ ): number {
129
+ let marginRatio;
130
+ switch (marginCategory) {
131
+ case 'Initial':
132
+ marginRatio = calculateSizePremiumLiabilityWeight(
133
+ size,
134
+ new BN(market.imfFactor),
135
+ new BN(market.marginRatioInitial),
136
+ MARGIN_PRECISION
137
+ ).toNumber();
138
+ break;
139
+ case 'Maintenance':
140
+ marginRatio = calculateSizePremiumLiabilityWeight(
141
+ size,
142
+ new BN(market.imfFactor),
143
+ new BN(market.marginRatioMaintenance),
144
+ MARGIN_PRECISION
145
+ ).toNumber();
146
+ break;
147
+ }
148
+
149
+ return marginRatio;
150
+ }
151
+
152
+ export function calculateUnrealizedAssetWeight(
153
+ market: PerpMarketAccount,
154
+ quoteSpotMarket: SpotMarketAccount,
155
+ unrealizedPnl: BN,
156
+ marginCategory: MarginCategory,
157
+ oraclePriceData: OraclePriceData
158
+ ): BN {
159
+ let assetWeight: BN;
160
+ switch (marginCategory) {
161
+ case 'Initial':
162
+ assetWeight = new BN(market.unrealizedPnlInitialAssetWeight);
163
+
164
+ if (market.unrealizedPnlMaxImbalance.gt(ZERO)) {
165
+ const netUnsettledPnl = calculateNetUserPnlImbalance(
166
+ market,
167
+ quoteSpotMarket,
168
+ oraclePriceData
169
+ );
170
+ if (netUnsettledPnl.gt(market.unrealizedPnlMaxImbalance)) {
171
+ assetWeight = assetWeight
172
+ .mul(market.unrealizedPnlMaxImbalance)
173
+ .div(netUnsettledPnl);
174
+ }
175
+ }
176
+
177
+ assetWeight = calculateSizeDiscountAssetWeight(
178
+ unrealizedPnl,
179
+ new BN(market.unrealizedPnlImfFactor),
180
+ assetWeight
181
+ );
182
+ break;
183
+ case 'Maintenance':
184
+ assetWeight = new BN(market.unrealizedPnlMaintenanceAssetWeight);
185
+ break;
186
+ }
187
+
188
+ return assetWeight;
189
+ }
190
+
191
+ export function calculateMarketAvailablePNL(
192
+ perpMarket: PerpMarketAccount,
193
+ spotMarket: SpotMarketAccount
194
+ ): BN {
195
+ return getTokenAmount(
196
+ perpMarket.pnlPool.scaledBalance,
197
+ spotMarket,
198
+ SpotBalanceType.DEPOSIT
199
+ );
200
+ }
201
+
202
+ export function calculateNetUserPnl(
203
+ perpMarket: PerpMarketAccount,
204
+ oraclePriceData: OraclePriceData
205
+ ): BN {
206
+ const netUserPositionValue = perpMarket.amm.baseAssetAmountWithAmm
207
+ .mul(oraclePriceData.price)
208
+ .div(BASE_PRECISION)
209
+ .div(PRICE_TO_QUOTE_PRECISION);
210
+
211
+ const netUserCostBasis = perpMarket.amm.quoteAssetAmount;
212
+
213
+ const netUserPnl = netUserPositionValue.add(netUserCostBasis);
214
+
215
+ return netUserPnl;
216
+ }
217
+
218
+ export function calculateNetUserPnlImbalance(
219
+ perpMarket: PerpMarketAccount,
220
+ spotMarket: SpotMarketAccount,
221
+ oraclePriceData: OraclePriceData
222
+ ): BN {
223
+ const netUserPnl = calculateNetUserPnl(perpMarket, oraclePriceData);
224
+
225
+ const pnlPool = getTokenAmount(
226
+ perpMarket.pnlPool.scaledBalance,
227
+ spotMarket,
228
+ SpotBalanceType.DEPOSIT
229
+ );
230
+
231
+ const imbalance = netUserPnl.sub(pnlPool);
232
+
233
+ return imbalance;
234
+ }
@@ -1,7 +1,29 @@
1
1
  import { AMM, OracleGuardRails } from '../types';
2
2
  import { OraclePriceData } from '../oracles/types';
3
- import { ONE, ZERO } from '../constants/numericConstants';
4
- import { BN } from '../index';
3
+ import {
4
+ BID_ASK_SPREAD_PRECISION,
5
+ MARGIN_PRECISION,
6
+ PRICE_PRECISION,
7
+ ONE,
8
+ ZERO,
9
+ } from '../constants/numericConstants';
10
+ import { BN, PerpMarketAccount } from '../index';
11
+ import { assert } from '../assert/assert';
12
+
13
+ export function oraclePriceBands(
14
+ market: PerpMarketAccount,
15
+ oraclePriceData: OraclePriceData
16
+ ): [BN, BN] {
17
+ const maxPercentDiff =
18
+ market.marginRatioInitial - market.marginRatioMaintenance;
19
+ const offset = oraclePriceData.price
20
+ .mul(new BN(maxPercentDiff))
21
+ .div(MARGIN_PRECISION);
22
+
23
+ assert(offset.gt(ZERO));
24
+
25
+ return [oraclePriceData.price.sub(offset), oraclePriceData.price.add(offset)];
26
+ }
5
27
 
6
28
  export function isOracleValid(
7
29
  amm: AMM,
@@ -9,22 +31,24 @@ export function isOracleValid(
9
31
  oracleGuardRails: OracleGuardRails,
10
32
  slot: number
11
33
  ): boolean {
12
- const isOraclePriceNonPositive = oraclePriceData.price.lt(ZERO);
34
+ const isOraclePriceNonPositive = oraclePriceData.price.lte(ZERO);
13
35
  const isOraclePriceTooVolatile =
14
36
  oraclePriceData.price
15
- .div(BN.max(ONE, amm.lastOraclePriceTwap))
37
+ .div(BN.max(ONE, amm.historicalOracleData.lastOraclePriceTwap))
16
38
  .gt(oracleGuardRails.validity.tooVolatileRatio) ||
17
- amm.lastOraclePriceTwap
39
+ amm.historicalOracleData.lastOraclePriceTwap
18
40
  .div(BN.max(ONE, oraclePriceData.price))
19
41
  .gt(oracleGuardRails.validity.tooVolatileRatio);
20
42
 
21
- const isConfidenceTooLarge = oraclePriceData.price
22
- .div(BN.max(ONE, oraclePriceData.confidence))
23
- .lt(oracleGuardRails.validity.confidenceIntervalMaxSize);
43
+ const isConfidenceTooLarge = new BN(amm.baseSpread)
44
+ .add(BN.max(ONE, oraclePriceData.confidence))
45
+ .mul(BID_ASK_SPREAD_PRECISION)
46
+ .div(oraclePriceData.price)
47
+ .gt(new BN(amm.maxSpread));
24
48
 
25
49
  const oracleIsStale = oraclePriceData.slot
26
50
  .sub(new BN(slot))
27
- .gt(oracleGuardRails.validity.slotsBeforeStale);
51
+ .gt(oracleGuardRails.validity.slotsBeforeStaleForAmm);
28
52
 
29
53
  return !(
30
54
  !oraclePriceData.hasSufficientNumberOfDataPoints ||
@@ -34,3 +58,33 @@ export function isOracleValid(
34
58
  isConfidenceTooLarge
35
59
  );
36
60
  }
61
+
62
+ export function isOracleTooDivergent(
63
+ amm: AMM,
64
+ oraclePriceData: OraclePriceData,
65
+ oracleGuardRails: OracleGuardRails,
66
+ now: BN
67
+ ): boolean {
68
+ const sinceLastUpdate = now.sub(
69
+ amm.historicalOracleData.lastOraclePriceTwapTs
70
+ );
71
+ const sinceStart = BN.max(ZERO, new BN(60 * 5).sub(sinceLastUpdate));
72
+ const oracleTwap5min = amm.historicalOracleData.lastOraclePriceTwap5min
73
+ .mul(sinceStart)
74
+ .add(oraclePriceData.price)
75
+ .mul(sinceLastUpdate)
76
+ .div(sinceStart.add(sinceLastUpdate));
77
+
78
+ const oracleSpread = oracleTwap5min.sub(oraclePriceData.price);
79
+ const oracleSpreadPct = oracleSpread.mul(PRICE_PRECISION).div(oracleTwap5min);
80
+
81
+ const tooDivergent = oracleSpreadPct
82
+ .abs()
83
+ .gte(
84
+ BID_ASK_SPREAD_PRECISION.mul(
85
+ oracleGuardRails.priceDivergence.markOracleDivergenceNumerator
86
+ ).div(oracleGuardRails.priceDivergence.markOracleDivergenceDenominator)
87
+ );
88
+
89
+ return tooDivergent;
90
+ }
@@ -1,21 +1,27 @@
1
- import { ClearingHouseUser } from '../clearingHouseUser';
2
- import { isOneOfVariant, isVariant, MarketAccount, Order } from '../types';
1
+ import { User } from '../user';
2
+ import {
3
+ isOneOfVariant,
4
+ isVariant,
5
+ PerpMarketAccount,
6
+ Order,
7
+ PositionDirection,
8
+ } from '../types';
3
9
  import { ZERO, TWO } from '../constants/numericConstants';
4
10
  import { BN } from '@project-serum/anchor';
5
11
  import { OraclePriceData } from '../oracles/types';
6
12
  import { getAuctionPrice, isAuctionComplete } from './auction';
7
- import { calculateAskPrice, calculateBidPrice } from './market';
13
+ import {
14
+ calculateMaxBaseAssetAmountFillable,
15
+ calculateMaxBaseAssetAmountToTrade,
16
+ } from './amm';
8
17
 
9
- export function isOrderRiskIncreasing(
10
- user: ClearingHouseUser,
11
- order: Order
12
- ): boolean {
18
+ export function isOrderRiskIncreasing(user: User, order: Order): boolean {
13
19
  if (isVariant(order.status, 'init')) {
14
20
  return false;
15
21
  }
16
22
 
17
23
  const position =
18
- user.getUserPosition(order.marketIndex) ||
24
+ user.getPerpPosition(order.marketIndex) ||
19
25
  user.getEmptyPosition(order.marketIndex);
20
26
 
21
27
  // if no position exists, it's risk increasing
@@ -48,7 +54,7 @@ export function isOrderRiskIncreasing(
48
54
  }
49
55
 
50
56
  export function isOrderRiskIncreasingInSameDirection(
51
- user: ClearingHouseUser,
57
+ user: User,
52
58
  order: Order
53
59
  ): boolean {
54
60
  if (isVariant(order.status, 'init')) {
@@ -56,7 +62,7 @@ export function isOrderRiskIncreasingInSameDirection(
56
62
  }
57
63
 
58
64
  const position =
59
- user.getUserPosition(order.marketIndex) ||
65
+ user.getPerpPosition(order.marketIndex) ||
60
66
  user.getEmptyPosition(order.marketIndex);
61
67
 
62
68
  // if no position exists, it's risk increasing
@@ -80,16 +86,13 @@ export function isOrderRiskIncreasingInSameDirection(
80
86
  return false;
81
87
  }
82
88
 
83
- export function isOrderReduceOnly(
84
- user: ClearingHouseUser,
85
- order: Order
86
- ): boolean {
89
+ export function isOrderReduceOnly(user: User, order: Order): boolean {
87
90
  if (isVariant(order.status, 'init')) {
88
91
  return false;
89
92
  }
90
93
 
91
94
  const position =
92
- user.getUserPosition(order.marketIndex) ||
95
+ user.getPerpPosition(order.marketIndex) ||
93
96
  user.getEmptyPosition(order.marketIndex);
94
97
 
95
98
  // if position is long and order is long
@@ -121,26 +124,25 @@ export function standardizeBaseAssetAmount(
121
124
 
122
125
  export function getLimitPrice(
123
126
  order: Order,
124
- market: MarketAccount,
125
127
  oraclePriceData: OraclePriceData,
126
128
  slot: number
127
129
  ): BN {
128
130
  let limitPrice;
129
- if (!order.oraclePriceOffset.eq(ZERO)) {
130
- limitPrice = oraclePriceData.price.add(order.oraclePriceOffset);
131
+ if (order.oraclePriceOffset !== 0) {
132
+ limitPrice = oraclePriceData.price.add(new BN(order.oraclePriceOffset));
131
133
  } else if (isOneOfVariant(order.orderType, ['market', 'triggerMarket'])) {
132
- if (isAuctionComplete(order, slot)) {
134
+ if (!isAuctionComplete(order, slot)) {
133
135
  limitPrice = getAuctionPrice(order, slot);
134
136
  } else if (!order.price.eq(ZERO)) {
135
137
  limitPrice = order.price;
136
- } else if (isVariant(order.direction, 'long')) {
137
- const askPrice = calculateAskPrice(market, oraclePriceData);
138
- const delta = askPrice.div(new BN(market.amm.maxSlippageRatio));
139
- limitPrice = askPrice.add(delta);
140
138
  } else {
141
- const bidPrice = calculateBidPrice(market, oraclePriceData);
142
- const delta = bidPrice.div(new BN(market.amm.maxSlippageRatio));
143
- limitPrice = bidPrice.sub(delta);
139
+ // check oracle validity?
140
+ const oraclePrice1Pct = oraclePriceData.price.div(new BN(100));
141
+ if (isVariant(order.direction, 'long')) {
142
+ limitPrice = oraclePriceData.price.add(oraclePrice1Pct);
143
+ } else {
144
+ limitPrice = oraclePriceData.price.sub(oraclePrice1Pct);
145
+ }
144
146
  }
145
147
  } else {
146
148
  limitPrice = order.price;
@@ -148,3 +150,138 @@ export function getLimitPrice(
148
150
 
149
151
  return limitPrice;
150
152
  }
153
+
154
+ export function getOptionalLimitPrice(
155
+ order: Order,
156
+ oraclePriceData: OraclePriceData,
157
+ slot: number
158
+ ): BN | undefined {
159
+ if (hasLimitPrice(order, slot)) {
160
+ return getLimitPrice(order, oraclePriceData, slot);
161
+ } else {
162
+ return undefined;
163
+ }
164
+ }
165
+
166
+ export function hasLimitPrice(order: Order, slot: number): boolean {
167
+ return (
168
+ order.price.gt(ZERO) ||
169
+ order.oraclePriceOffset != 0 ||
170
+ !isAuctionComplete(order, slot)
171
+ );
172
+ }
173
+
174
+ export function isFillableByVAMM(
175
+ order: Order,
176
+ market: PerpMarketAccount,
177
+ oraclePriceData: OraclePriceData,
178
+ slot: number,
179
+ ts: number
180
+ ): boolean {
181
+ return (
182
+ (isAuctionComplete(order, slot) &&
183
+ !calculateBaseAssetAmountForAmmToFulfill(
184
+ order,
185
+ market,
186
+ oraclePriceData,
187
+ slot
188
+ ).eq(ZERO)) ||
189
+ isOrderExpired(order, ts)
190
+ );
191
+ }
192
+
193
+ export function calculateBaseAssetAmountForAmmToFulfill(
194
+ order: Order,
195
+ market: PerpMarketAccount,
196
+ oraclePriceData: OraclePriceData,
197
+ slot: number
198
+ ): BN {
199
+ if (
200
+ isOneOfVariant(order.orderType, ['triggerMarket', 'triggerLimit']) &&
201
+ order.triggered === false
202
+ ) {
203
+ return ZERO;
204
+ }
205
+
206
+ const limitPrice = getOptionalLimitPrice(order, oraclePriceData, slot);
207
+ let baseAssetAmount;
208
+ if (limitPrice !== undefined) {
209
+ baseAssetAmount = calculateBaseAssetAmountToFillUpToLimitPrice(
210
+ order,
211
+ market,
212
+ limitPrice,
213
+ oraclePriceData
214
+ );
215
+ } else {
216
+ baseAssetAmount = order.baseAssetAmount.sub(order.baseAssetAmountFilled);
217
+ }
218
+
219
+ const maxBaseAssetAmount = calculateMaxBaseAssetAmountFillable(
220
+ market.amm,
221
+ order.direction
222
+ );
223
+
224
+ return BN.min(maxBaseAssetAmount, baseAssetAmount);
225
+ }
226
+
227
+ export function calculateBaseAssetAmountToFillUpToLimitPrice(
228
+ order: Order,
229
+ market: PerpMarketAccount,
230
+ limitPrice: BN,
231
+ oraclePriceData: OraclePriceData
232
+ ): BN {
233
+ const [maxAmountToTrade, direction] = calculateMaxBaseAssetAmountToTrade(
234
+ market.amm,
235
+ limitPrice,
236
+ order.direction,
237
+ oraclePriceData
238
+ );
239
+
240
+ const baseAssetAmount = standardizeBaseAssetAmount(
241
+ maxAmountToTrade,
242
+ market.amm.orderStepSize
243
+ );
244
+
245
+ // Check that directions are the same
246
+ const sameDirection = isSameDirection(direction, order.direction);
247
+ if (!sameDirection) {
248
+ return ZERO;
249
+ }
250
+
251
+ const baseAssetAmountUnfilled = order.baseAssetAmount.sub(
252
+ order.baseAssetAmountFilled
253
+ );
254
+ return baseAssetAmount.gt(baseAssetAmountUnfilled)
255
+ ? baseAssetAmountUnfilled
256
+ : baseAssetAmount;
257
+ }
258
+
259
+ function isSameDirection(
260
+ firstDirection: PositionDirection,
261
+ secondDirection: PositionDirection
262
+ ): boolean {
263
+ return (
264
+ (isVariant(firstDirection, 'long') && isVariant(secondDirection, 'long')) ||
265
+ (isVariant(firstDirection, 'short') && isVariant(secondDirection, 'short'))
266
+ );
267
+ }
268
+
269
+ export function isOrderExpired(order: Order, ts: number): boolean {
270
+ if (
271
+ isOneOfVariant(order.orderType, ['triggerMarket', 'triggerLimit']) ||
272
+ !isVariant(order.status, 'open') ||
273
+ order.maxTs.eq(ZERO)
274
+ ) {
275
+ return false;
276
+ }
277
+
278
+ return new BN(ts).gt(order.maxTs);
279
+ }
280
+
281
+ export function isMarketOrder(order: Order): boolean {
282
+ return isOneOfVariant(order.orderType, ['market', 'triggerMarket']);
283
+ }
284
+
285
+ export function isLimitOrder(order: Order): boolean {
286
+ return isOneOfVariant(order.orderType, ['limit', 'triggerLimit']);
287
+ }