@drift-labs/sdk 0.2.0-master.3 → 0.2.0-master.30

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 (208) hide show
  1. package/README.md +13 -13
  2. package/lib/accounts/bulkUserStatsSubscription.d.ts +7 -0
  3. package/lib/accounts/bulkUserStatsSubscription.js +21 -0
  4. package/lib/accounts/bulkUserSubscription.js +0 -1
  5. package/lib/accounts/fetch.d.ts +2 -1
  6. package/lib/accounts/fetch.js +9 -1
  7. package/lib/accounts/pollingClearingHouseAccountSubscriber.d.ts +16 -16
  8. package/lib/accounts/pollingClearingHouseAccountSubscriber.js +31 -28
  9. package/lib/accounts/pollingUserStatsAccountSubscriber.d.ts +27 -0
  10. package/lib/accounts/pollingUserStatsAccountSubscriber.js +113 -0
  11. package/lib/accounts/types.d.ts +22 -11
  12. package/lib/accounts/webSocketClearingHouseAccountSubscriber.d.ts +17 -17
  13. package/lib/accounts/webSocketClearingHouseAccountSubscriber.js +37 -35
  14. package/lib/accounts/webSocketUserStatsAccountSubsriber.d.ts +20 -0
  15. package/lib/accounts/webSocketUserStatsAccountSubsriber.js +47 -0
  16. package/lib/addresses/marketAddresses.d.ts +1 -3
  17. package/lib/addresses/pda.d.ts +10 -4
  18. package/lib/addresses/pda.js +51 -14
  19. package/lib/admin.d.ts +34 -22
  20. package/lib/admin.js +182 -73
  21. package/lib/clearingHouse.d.ts +120 -42
  22. package/lib/clearingHouse.js +1505 -254
  23. package/lib/clearingHouseConfig.d.ts +4 -4
  24. package/lib/clearingHouseUser.d.ts +50 -38
  25. package/lib/clearingHouseUser.js +410 -190
  26. package/lib/clearingHouseUserStats.d.ts +18 -0
  27. package/lib/clearingHouseUserStats.js +49 -0
  28. package/lib/clearingHouseUserStatsConfig.d.ts +14 -0
  29. package/lib/clearingHouseUserStatsConfig.js +2 -0
  30. package/lib/config.d.ts +7 -9
  31. package/lib/config.js +21 -21
  32. package/lib/constants/numericConstants.d.ts +18 -12
  33. package/lib/constants/numericConstants.js +28 -21
  34. package/lib/constants/perpMarkets.d.ts +18 -0
  35. package/lib/constants/{markets.js → perpMarkets.js} +7 -7
  36. package/lib/constants/spotMarkets.d.ts +19 -0
  37. package/lib/constants/spotMarkets.js +53 -0
  38. package/lib/dlob/DLOB.d.ts +73 -0
  39. package/lib/dlob/DLOB.js +553 -0
  40. package/lib/dlob/DLOBNode.d.ts +52 -0
  41. package/lib/dlob/DLOBNode.js +82 -0
  42. package/lib/dlob/NodeList.d.ts +26 -0
  43. package/lib/dlob/NodeList.js +138 -0
  44. package/lib/events/eventList.js +3 -0
  45. package/lib/events/eventSubscriber.d.ts +4 -2
  46. package/lib/events/eventSubscriber.js +16 -9
  47. package/lib/events/fetchLogs.d.ts +10 -1
  48. package/lib/events/fetchLogs.js +27 -7
  49. package/lib/events/pollingLogProvider.d.ts +2 -1
  50. package/lib/events/pollingLogProvider.js +6 -2
  51. package/lib/events/sort.js +8 -11
  52. package/lib/events/types.d.ts +7 -2
  53. package/lib/events/types.js +5 -0
  54. package/lib/examples/makeTradeExample.js +20 -8
  55. package/lib/factory/bigNum.d.ts +1 -0
  56. package/lib/factory/bigNum.js +34 -10
  57. package/lib/idl/clearing_house.json +4313 -1433
  58. package/lib/idl/{mock_usdc_faucet.json → token_faucet.json} +46 -23
  59. package/lib/index.d.ts +21 -6
  60. package/lib/index.js +25 -6
  61. package/lib/math/amm.d.ts +8 -5
  62. package/lib/math/amm.js +68 -46
  63. package/lib/math/auction.js +4 -1
  64. package/lib/math/conversion.js +1 -1
  65. package/lib/math/funding.d.ts +6 -6
  66. package/lib/math/funding.js +11 -10
  67. package/lib/math/insurance.d.ts +4 -0
  68. package/lib/math/insurance.js +27 -0
  69. package/lib/math/margin.d.ts +11 -0
  70. package/lib/math/margin.js +77 -0
  71. package/lib/math/market.d.ts +14 -9
  72. package/lib/math/market.js +69 -7
  73. package/lib/math/oracles.d.ts +4 -0
  74. package/lib/math/oracles.js +36 -8
  75. package/lib/math/orders.d.ts +6 -2
  76. package/lib/math/orders.js +78 -3
  77. package/lib/math/position.d.ts +21 -13
  78. package/lib/math/position.js +76 -36
  79. package/lib/math/repeg.js +14 -5
  80. package/lib/math/spotBalance.d.ts +22 -0
  81. package/lib/math/spotBalance.js +193 -0
  82. package/lib/math/spotMarket.d.ts +4 -0
  83. package/lib/math/spotMarket.js +8 -0
  84. package/lib/math/spotPosition.d.ts +6 -0
  85. package/lib/math/spotPosition.js +23 -0
  86. package/lib/math/trade.d.ts +10 -10
  87. package/lib/math/trade.js +22 -29
  88. package/lib/oracles/pythClient.js +1 -1
  89. package/lib/oracles/quoteAssetOracleClient.js +1 -1
  90. package/lib/oracles/switchboardClient.js +1 -1
  91. package/lib/orderParams.d.ts +14 -5
  92. package/lib/orderParams.js +12 -92
  93. package/lib/serum/serumSubscriber.d.ts +23 -0
  94. package/lib/serum/serumSubscriber.js +41 -0
  95. package/lib/serum/types.d.ts +11 -0
  96. package/lib/serum/types.js +2 -0
  97. package/lib/slot/SlotSubscriber.d.ts +7 -0
  98. package/lib/slot/SlotSubscriber.js +3 -0
  99. package/lib/{mockUSDCFaucet.d.ts → tokenFaucet.d.ts} +8 -5
  100. package/lib/{mockUSDCFaucet.js → tokenFaucet.js} +63 -51
  101. package/lib/tx/retryTxSender.d.ts +1 -1
  102. package/lib/tx/retryTxSender.js +13 -4
  103. package/lib/tx/types.d.ts +1 -1
  104. package/lib/tx/utils.js +1 -1
  105. package/lib/types.d.ts +474 -123
  106. package/lib/types.js +99 -5
  107. package/lib/userMap/userMap.d.ts +25 -0
  108. package/lib/userMap/userMap.js +73 -0
  109. package/lib/userMap/userStatsMap.d.ts +19 -0
  110. package/lib/userMap/userStatsMap.js +68 -0
  111. package/lib/util/computeUnits.js +1 -1
  112. package/lib/util/getTokenAddress.d.ts +2 -0
  113. package/lib/util/getTokenAddress.js +9 -0
  114. package/package.json +9 -5
  115. package/src/accounts/bulkUserStatsSubscription.ts +33 -0
  116. package/src/accounts/bulkUserSubscription.ts +0 -1
  117. package/src/accounts/fetch.ts +27 -2
  118. package/src/accounts/pollingClearingHouseAccountSubscriber.ts +46 -42
  119. package/src/accounts/pollingUserStatsAccountSubscriber.ts +172 -0
  120. package/src/accounts/types.ts +31 -11
  121. package/src/accounts/webSocketClearingHouseAccountSubscriber.ts +64 -59
  122. package/src/accounts/webSocketUserStatsAccountSubsriber.ts +80 -0
  123. package/src/addresses/marketAddresses.ts +1 -2
  124. package/src/addresses/pda.ts +88 -14
  125. package/src/admin.ts +333 -128
  126. package/src/assert/assert.js +9 -0
  127. package/src/clearingHouse.ts +2464 -458
  128. package/src/clearingHouseConfig.ts +4 -3
  129. package/src/clearingHouseUser.ts +747 -291
  130. package/src/clearingHouseUserStats.ts +75 -0
  131. package/src/clearingHouseUserStatsConfig.ts +18 -0
  132. package/src/config.ts +30 -31
  133. package/src/constants/numericConstants.ts +41 -25
  134. package/src/constants/{markets.ts → perpMarkets.ts} +10 -10
  135. package/src/constants/spotMarkets.ts +72 -0
  136. package/src/dlob/DLOB.ts +868 -0
  137. package/src/dlob/DLOBNode.ts +162 -0
  138. package/src/dlob/NodeList.ts +185 -0
  139. package/src/events/eventList.js +77 -0
  140. package/src/events/eventList.ts +3 -0
  141. package/src/events/eventSubscriber.ts +20 -12
  142. package/src/events/fetchLogs.ts +35 -8
  143. package/src/events/pollingLogProvider.ts +10 -2
  144. package/src/events/sort.ts +11 -15
  145. package/src/events/types.ts +16 -1
  146. package/src/examples/makeTradeExample.js +157 -0
  147. package/src/examples/makeTradeExample.ts +32 -14
  148. package/src/factory/bigNum.ts +42 -13
  149. package/src/idl/clearing_house.json +4313 -1433
  150. package/src/idl/{mock_usdc_faucet.json → token_faucet.json} +46 -23
  151. package/src/index.ts +21 -6
  152. package/src/math/amm.ts +136 -66
  153. package/src/math/auction.ts +5 -1
  154. package/src/math/conversion.ts +2 -2
  155. package/src/math/funding.ts +20 -18
  156. package/src/math/insurance.ts +35 -0
  157. package/src/math/margin.ts +127 -0
  158. package/src/math/market.ts +138 -12
  159. package/src/math/oracles.ts +63 -9
  160. package/src/math/orders.ts +138 -4
  161. package/src/math/position.ts +119 -58
  162. package/src/math/repeg.ts +16 -6
  163. package/src/math/spotBalance.ts +316 -0
  164. package/src/math/spotMarket.ts +9 -0
  165. package/src/math/spotPosition.ts +47 -0
  166. package/src/math/trade.ts +43 -49
  167. package/src/oracles/pythClient.ts +2 -2
  168. package/src/oracles/quoteAssetOracleClient.ts +2 -2
  169. package/src/oracles/switchboardClient.ts +2 -2
  170. package/src/orderParams.ts +24 -137
  171. package/src/serum/serumSubscriber.ts +80 -0
  172. package/src/serum/types.ts +13 -0
  173. package/src/slot/SlotSubscriber.ts +11 -1
  174. package/src/token/index.js +38 -0
  175. package/src/{mockUSDCFaucet.ts → tokenFaucet.ts} +82 -70
  176. package/src/tx/retryTxSender.ts +16 -5
  177. package/src/tx/types.js +2 -0
  178. package/src/tx/types.ts +2 -1
  179. package/src/tx/utils.js +17 -0
  180. package/src/tx/utils.ts +1 -1
  181. package/src/types.ts +477 -125
  182. package/src/userMap/userMap.ts +100 -0
  183. package/src/userMap/userStatsMap.ts +110 -0
  184. package/src/util/computeUnits.js +21 -11
  185. package/src/util/computeUnits.ts +1 -1
  186. package/src/util/getTokenAddress.js +9 -0
  187. package/src/util/getTokenAddress.ts +18 -0
  188. package/src/util/promiseTimeout.js +14 -0
  189. package/src/util/tps.js +27 -0
  190. package/tests/bn/test.ts +12 -3
  191. package/tests/dlob/helpers.ts +374 -0
  192. package/tests/dlob/test.ts +2865 -0
  193. package/lib/constants/banks.d.ts +0 -16
  194. package/lib/constants/banks.js +0 -34
  195. package/lib/constants/markets.d.ts +0 -19
  196. package/lib/math/bankBalance.d.ts +0 -9
  197. package/lib/math/bankBalance.js +0 -75
  198. package/lib/math/state.d.ts +0 -8
  199. package/lib/math/state.js +0 -15
  200. package/lib/orders.d.ts +0 -8
  201. package/lib/orders.js +0 -134
  202. package/src/constants/banks.ts +0 -43
  203. package/src/math/bankBalance.ts +0 -112
  204. package/src/math/state.ts +0 -14
  205. package/src/math/utils.js +0 -27
  206. package/src/math/utils.js.map +0 -1
  207. package/src/orders.ts +0 -244
  208. package/src/util/computeUnits.js.map +0 -1
@@ -0,0 +1,35 @@
1
+ import { ZERO } from '../constants/numericConstants';
2
+ import { BN } from '../index';
3
+
4
+ export function stakeAmountToShares(
5
+ amount: BN,
6
+ totalIfShares: BN,
7
+ insuranceFundVaultBalance: BN
8
+ ): BN {
9
+ let nShares: BN;
10
+ if (insuranceFundVaultBalance.gt(ZERO)) {
11
+ nShares = amount.mul(totalIfShares).div(insuranceFundVaultBalance);
12
+ } else {
13
+ nShares = amount;
14
+ }
15
+
16
+ return nShares;
17
+ }
18
+
19
+ export function unstakeSharesToAmount(
20
+ nShares: BN,
21
+ totalIfShares: BN,
22
+ insuranceFundVaultBalance: BN
23
+ ): BN {
24
+ let amount: BN;
25
+ if (totalIfShares.gt(ZERO)) {
26
+ amount = BN.max(
27
+ ZERO,
28
+ nShares.mul(insuranceFundVaultBalance).div(totalIfShares)
29
+ );
30
+ } else {
31
+ amount = ZERO;
32
+ }
33
+
34
+ return amount;
35
+ }
@@ -0,0 +1,127 @@
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 { assert } from '../assert/assert';
13
+
14
+ export function calculateSizePremiumLiabilityWeight(
15
+ size: BN, // AMM_RESERVE_PRECISION
16
+ imfFactor: BN,
17
+ liabilityWeight: BN,
18
+ precision: BN
19
+ ): BN {
20
+ if (imfFactor.eq(ZERO)) {
21
+ return liabilityWeight;
22
+ }
23
+
24
+ const sizeSqrt = squareRootBN(size.mul(new BN(10)).add(new BN(1))); //1e9 -> 1e10 -> 1e5
25
+
26
+ const denom0 = BN.max(new BN(1), SPOT_MARKET_IMF_PRECISION.div(imfFactor));
27
+ assert(denom0.gt(ZERO));
28
+ const liabilityWeightNumerator = liabilityWeight.sub(
29
+ liabilityWeight.div(
30
+ BN.max(new BN(1), SPOT_MARKET_IMF_PRECISION.div(imfFactor))
31
+ )
32
+ );
33
+
34
+ const denom = new BN(100_000).mul(SPOT_MARKET_IMF_PRECISION).div(precision);
35
+ assert(denom.gt(ZERO));
36
+
37
+ const sizePremiumLiabilityWeight = liabilityWeightNumerator.add(
38
+ sizeSqrt // 1e5
39
+ .mul(imfFactor)
40
+ .div(denom) // 1e5
41
+ );
42
+
43
+ const maxLiabilityWeight = BN.max(
44
+ liabilityWeight,
45
+ sizePremiumLiabilityWeight
46
+ );
47
+ return maxLiabilityWeight;
48
+ }
49
+
50
+ export function calculateSizeDiscountAssetWeight(
51
+ size: BN, // AMM_RESERVE_PRECISION
52
+ imfFactor: BN,
53
+ assetWeight: BN
54
+ ): BN {
55
+ if (imfFactor.eq(ZERO)) {
56
+ return assetWeight;
57
+ }
58
+
59
+ const sizeSqrt = squareRootBN(size.mul(new BN(10)).add(new BN(1))); //1e9 -> 1e10 -> 1e5
60
+ const imfNumerator = SPOT_MARKET_IMF_PRECISION.add(
61
+ SPOT_MARKET_IMF_PRECISION.div(new BN(10))
62
+ );
63
+
64
+ const sizeDiscountAssetWeight = imfNumerator
65
+ .mul(SPOT_MARKET_WEIGHT_PRECISION)
66
+ .div(
67
+ SPOT_MARKET_IMF_PRECISION.add(
68
+ sizeSqrt // 1e5
69
+ .mul(imfFactor)
70
+ .div(new BN(100_000)) // 1e5
71
+ )
72
+ );
73
+
74
+ const minAssetWeight = BN.min(assetWeight, sizeDiscountAssetWeight);
75
+
76
+ return minAssetWeight;
77
+ }
78
+
79
+ export function calculateOraclePriceForPerpMargin(
80
+ perpPosition: PerpPosition,
81
+ market: PerpMarketAccount,
82
+ oraclePriceData: OraclePriceData
83
+ ): BN {
84
+ const oraclePriceOffset = BN.min(
85
+ new BN(market.amm.maxSpread)
86
+ .mul(oraclePriceData.price)
87
+ .div(BID_ASK_SPREAD_PRECISION),
88
+ oraclePriceData.confidence.add(
89
+ new BN(market.amm.baseSpread)
90
+ .mul(oraclePriceData.price)
91
+ .div(BID_ASK_SPREAD_PRECISION)
92
+ )
93
+ );
94
+
95
+ let marginPrice: BN;
96
+ if (perpPosition.baseAssetAmount.gt(ZERO)) {
97
+ marginPrice = oraclePriceData.price.sub(oraclePriceOffset);
98
+ } else {
99
+ marginPrice = oraclePriceData.price.add(oraclePriceOffset);
100
+ }
101
+
102
+ return marginPrice;
103
+ }
104
+
105
+ export function calculateBaseAssetValueWithOracle(
106
+ market: PerpMarketAccount,
107
+ perpPosition: PerpPosition,
108
+ oraclePriceData: OraclePriceData
109
+ ): BN {
110
+ return perpPosition.baseAssetAmount
111
+ .abs()
112
+ .mul(oraclePriceData.price)
113
+ .div(AMM_RESERVE_PRECISION);
114
+ }
115
+
116
+ export function calculateWorstCaseBaseAssetAmount(
117
+ perpPosition: PerpPosition
118
+ ): BN {
119
+ const allBids = perpPosition.baseAssetAmount.add(perpPosition.openBids);
120
+ const allAsks = perpPosition.baseAssetAmount.add(perpPosition.openAsks);
121
+
122
+ if (allBids.abs().gt(allAsks.abs())) {
123
+ return allBids;
124
+ } else {
125
+ return allAsks;
126
+ }
127
+ }
@@ -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,
@@ -90,10 +107,10 @@ export function calculateNewMarketAfterTrade(
90
107
  }
91
108
 
92
109
  export function calculateMarkOracleSpread(
93
- market: MarketAccount,
110
+ market: PerpMarketAccount,
94
111
  oraclePriceData: OraclePriceData
95
112
  ): BN {
96
- const markPrice = calculateMarkPrice(market, oraclePriceData);
113
+ const markPrice = calculateReservePrice(market, oraclePriceData);
97
114
  return calculateOracleSpread(markPrice, oraclePriceData);
98
115
  }
99
116
 
@@ -103,3 +120,112 @@ 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
+ market.imfFactor,
135
+ new BN(market.marginRatioInitial),
136
+ MARGIN_PRECISION
137
+ ).toNumber();
138
+ break;
139
+ case 'Maintenance':
140
+ marginRatio = market.marginRatioMaintenance;
141
+ break;
142
+ }
143
+
144
+ return marginRatio;
145
+ }
146
+
147
+ export function calculateUnrealizedAssetWeight(
148
+ market: PerpMarketAccount,
149
+ quoteSpotMarket: SpotMarketAccount,
150
+ unrealizedPnl: BN,
151
+ marginCategory: MarginCategory,
152
+ oraclePriceData: OraclePriceData
153
+ ): BN {
154
+ let assetWeight: BN;
155
+ switch (marginCategory) {
156
+ case 'Initial':
157
+ assetWeight = new BN(market.unrealizedInitialAssetWeight);
158
+
159
+ if (market.unrealizedMaxImbalance.gt(ZERO)) {
160
+ const netUnsettledPnl = calculateNetUserPnlImbalance(
161
+ market,
162
+ quoteSpotMarket,
163
+ oraclePriceData
164
+ );
165
+ if (netUnsettledPnl.gt(market.unrealizedMaxImbalance)) {
166
+ assetWeight = assetWeight
167
+ .mul(market.unrealizedMaxImbalance)
168
+ .div(netUnsettledPnl);
169
+ }
170
+ }
171
+
172
+ assetWeight = calculateSizeDiscountAssetWeight(
173
+ unrealizedPnl,
174
+ market.unrealizedImfFactor,
175
+ assetWeight
176
+ );
177
+ break;
178
+ case 'Maintenance':
179
+ assetWeight = new BN(market.unrealizedMaintenanceAssetWeight);
180
+ break;
181
+ }
182
+
183
+ return assetWeight;
184
+ }
185
+
186
+ export function calculateMarketAvailablePNL(
187
+ perpMarket: PerpMarketAccount,
188
+ spotMarket: SpotMarketAccount
189
+ ): BN {
190
+ return getTokenAmount(
191
+ perpMarket.pnlPool.balance,
192
+ spotMarket,
193
+ SpotBalanceType.DEPOSIT
194
+ );
195
+ }
196
+
197
+ export function calculateNetUserPnl(
198
+ perpMarket: PerpMarketAccount,
199
+ oraclePriceData: OraclePriceData
200
+ ): BN {
201
+ const netUserPositionValue = perpMarket.amm.netBaseAssetAmount
202
+ .mul(oraclePriceData.price)
203
+ .div(BASE_PRECISION)
204
+ .div(PRICE_TO_QUOTE_PRECISION);
205
+
206
+ const netUserCostBasis = perpMarket.amm.quoteAssetAmountLong
207
+ .add(perpMarket.amm.quoteAssetAmountShort)
208
+ .sub(perpMarket.amm.cumulativeSocialLoss);
209
+
210
+ const netUserPnl = netUserPositionValue.add(netUserCostBasis);
211
+
212
+ return netUserPnl;
213
+ }
214
+
215
+ export function calculateNetUserPnlImbalance(
216
+ perpMarket: PerpMarketAccount,
217
+ spotMarket: SpotMarketAccount,
218
+ oraclePriceData: OraclePriceData
219
+ ): BN {
220
+ const netUserPnl = calculateNetUserPnl(perpMarket, oraclePriceData);
221
+
222
+ const pnlPool = getTokenAmount(
223
+ perpMarket.pnlPool.balance,
224
+ spotMarket,
225
+ SpotBalanceType.DEPOSIT
226
+ );
227
+
228
+ const imbalance = netUserPnl.sub(pnlPool);
229
+
230
+ return imbalance;
231
+ }
@@ -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,9 +1,20 @@
1
1
  import { ClearingHouseUser } from '../clearingHouseUser';
2
- import { isOneOfVariant, isVariant, Order } from '../types';
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
- import { getAuctionPrice } from './auction';
12
+ import { getAuctionPrice, isAuctionComplete } from './auction';
13
+ import { calculateAskPrice, calculateBidPrice } from './market';
14
+ import {
15
+ calculateMaxBaseAssetAmountFillable,
16
+ calculateMaxBaseAssetAmountToTrade,
17
+ } from './amm';
7
18
 
8
19
  export function isOrderRiskIncreasing(
9
20
  user: ClearingHouseUser,
@@ -121,16 +132,139 @@ export function standardizeBaseAssetAmount(
121
132
  export function getLimitPrice(
122
133
  order: Order,
123
134
  oraclePriceData: OraclePriceData,
124
- slot: number
135
+ slot: number,
136
+ perpMarket?: PerpMarketAccount
125
137
  ): BN {
126
138
  let limitPrice;
127
139
  if (!order.oraclePriceOffset.eq(ZERO)) {
128
140
  limitPrice = oraclePriceData.price.add(order.oraclePriceOffset);
129
141
  } else if (isOneOfVariant(order.orderType, ['market', 'triggerMarket'])) {
130
- limitPrice = getAuctionPrice(order, slot);
142
+ if (!isAuctionComplete(order, slot)) {
143
+ limitPrice = getAuctionPrice(order, slot);
144
+ } else if (!order.price.eq(ZERO)) {
145
+ limitPrice = order.price;
146
+ } else {
147
+ if (perpMarket) {
148
+ if (isVariant(order.direction, 'long')) {
149
+ const askPrice = calculateAskPrice(perpMarket, oraclePriceData);
150
+ const delta = askPrice.div(new BN(perpMarket.amm.maxSlippageRatio));
151
+ limitPrice = askPrice.add(delta);
152
+ } else {
153
+ const bidPrice = calculateBidPrice(perpMarket, oraclePriceData);
154
+ const delta = bidPrice.div(new BN(perpMarket.amm.maxSlippageRatio));
155
+ limitPrice = bidPrice.sub(delta);
156
+ }
157
+ } else {
158
+ // check oracle validity?
159
+ const oraclePrice1Pct = oraclePriceData.price.div(new BN(100));
160
+ if (isVariant(order.direction, 'long')) {
161
+ limitPrice = oraclePriceData.price.add(oraclePrice1Pct);
162
+ } else {
163
+ limitPrice = oraclePriceData.price.sub(oraclePrice1Pct);
164
+ }
165
+ }
166
+ }
131
167
  } else {
132
168
  limitPrice = order.price;
133
169
  }
134
170
 
135
171
  return limitPrice;
136
172
  }
173
+
174
+ export function isFillableByVAMM(
175
+ order: Order,
176
+ market: PerpMarketAccount,
177
+ oraclePriceData: OraclePriceData,
178
+ slot: number
179
+ ): boolean {
180
+ return (
181
+ (isAuctionComplete(order, slot) &&
182
+ !calculateBaseAssetAmountForAmmToFulfill(
183
+ order,
184
+ market,
185
+ oraclePriceData,
186
+ slot
187
+ ).eq(ZERO)) ||
188
+ isOrderExpired(order, slot)
189
+ );
190
+ }
191
+
192
+ export function calculateBaseAssetAmountForAmmToFulfill(
193
+ order: Order,
194
+ market: PerpMarketAccount,
195
+ oraclePriceData: OraclePriceData,
196
+ slot: number
197
+ ): BN {
198
+ if (
199
+ isOneOfVariant(order.orderType, ['triggerMarket', 'triggerLimit']) &&
200
+ order.triggered === false
201
+ ) {
202
+ return ZERO;
203
+ }
204
+
205
+ const limitPrice = getLimitPrice(order, oraclePriceData, slot, market);
206
+ const baseAssetAmount = calculateBaseAssetAmountToFillUpToLimitPrice(
207
+ order,
208
+ market,
209
+ limitPrice,
210
+ oraclePriceData
211
+ );
212
+
213
+ const maxBaseAssetAmount = calculateMaxBaseAssetAmountFillable(
214
+ market.amm,
215
+ order.direction
216
+ );
217
+
218
+ return BN.min(maxBaseAssetAmount, baseAssetAmount);
219
+ }
220
+
221
+ export function calculateBaseAssetAmountToFillUpToLimitPrice(
222
+ order: Order,
223
+ market: PerpMarketAccount,
224
+ limitPrice: BN,
225
+ oraclePriceData: OraclePriceData
226
+ ): BN {
227
+ const [maxAmountToTrade, direction] = calculateMaxBaseAssetAmountToTrade(
228
+ market.amm,
229
+ limitPrice,
230
+ order.direction,
231
+ oraclePriceData
232
+ );
233
+
234
+ const baseAssetAmount = standardizeBaseAssetAmount(
235
+ maxAmountToTrade,
236
+ market.amm.baseAssetAmountStepSize
237
+ );
238
+
239
+ // Check that directions are the same
240
+ const sameDirection = isSameDirection(direction, order.direction);
241
+ if (!sameDirection) {
242
+ return ZERO;
243
+ }
244
+
245
+ return baseAssetAmount.gt(order.baseAssetAmount)
246
+ ? order.baseAssetAmount
247
+ : baseAssetAmount;
248
+ }
249
+
250
+ function isSameDirection(
251
+ firstDirection: PositionDirection,
252
+ secondDirection: PositionDirection
253
+ ): boolean {
254
+ return (
255
+ (isVariant(firstDirection, 'long') && isVariant(secondDirection, 'long')) ||
256
+ (isVariant(firstDirection, 'short') && isVariant(secondDirection, 'short'))
257
+ );
258
+ }
259
+
260
+ export function isOrderExpired(order: Order, slot: number): boolean {
261
+ if (
262
+ isOneOfVariant(order.orderType, ['triggerMarket', 'triggerLimit']) ||
263
+ !isVariant(order.status, 'open') ||
264
+ order.timeInForce === 0
265
+ ) {
266
+ return false;
267
+ }
268
+
269
+ return new BN(slot).sub(order.slot).gt(new BN(order.timeInForce));
270
+ }