@drift-labs/sdk 0.1.36-master.7 → 0.2.0-master.2

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 (197) hide show
  1. package/lib/accounts/bulkAccountLoader.d.ts +6 -6
  2. package/lib/accounts/bulkAccountLoader.js +81 -95
  3. package/lib/accounts/bulkUserSubscription.js +13 -57
  4. package/lib/accounts/fetch.d.ts +4 -0
  5. package/lib/accounts/fetch.js +18 -0
  6. package/lib/accounts/pollingClearingHouseAccountSubscriber.d.ts +34 -38
  7. package/lib/accounts/pollingClearingHouseAccountSubscriber.js +224 -224
  8. package/lib/accounts/pollingOracleSubscriber.d.ts +3 -3
  9. package/lib/accounts/pollingOracleSubscriber.js +37 -49
  10. package/lib/accounts/pollingTokenAccountSubscriber.d.ts +3 -3
  11. package/lib/accounts/pollingTokenAccountSubscriber.js +35 -50
  12. package/lib/accounts/pollingUserAccountSubscriber.d.ts +7 -13
  13. package/lib/accounts/pollingUserAccountSubscriber.js +71 -134
  14. package/lib/accounts/types.d.ts +34 -41
  15. package/lib/accounts/webSocketAccountSubscriber.d.ts +6 -4
  16. package/lib/accounts/webSocketAccountSubscriber.js +39 -35
  17. package/lib/accounts/webSocketClearingHouseAccountSubscriber.d.ts +33 -28
  18. package/lib/accounts/webSocketClearingHouseAccountSubscriber.js +153 -185
  19. package/lib/accounts/webSocketUserAccountSubscriber.d.ts +5 -11
  20. package/lib/accounts/webSocketUserAccountSubscriber.js +22 -67
  21. package/lib/addresses/marketAddresses.d.ts +4 -0
  22. package/lib/addresses/marketAddresses.js +15 -0
  23. package/lib/addresses/pda.d.ts +12 -0
  24. package/lib/addresses/pda.js +83 -0
  25. package/lib/admin.d.ts +9 -17
  26. package/lib/admin.js +375 -558
  27. package/lib/clearingHouse.d.ts +86 -109
  28. package/lib/clearingHouse.js +828 -895
  29. package/lib/clearingHouseConfig.d.ts +34 -0
  30. package/lib/clearingHouseConfig.js +2 -0
  31. package/lib/clearingHouseUser.d.ts +19 -22
  32. package/lib/clearingHouseUser.js +155 -141
  33. package/lib/clearingHouseUserConfig.d.ts +14 -0
  34. package/lib/clearingHouseUserConfig.js +2 -0
  35. package/lib/config.d.ts +12 -0
  36. package/lib/config.js +35 -4
  37. package/lib/constants/banks.d.ts +16 -0
  38. package/lib/constants/banks.js +34 -0
  39. package/lib/constants/markets.d.ts +8 -3
  40. package/lib/constants/markets.js +13 -206
  41. package/lib/constants/numericConstants.d.ts +17 -0
  42. package/lib/constants/numericConstants.js +23 -6
  43. package/lib/events/eventList.d.ts +22 -0
  44. package/lib/events/eventList.js +77 -0
  45. package/lib/events/eventSubscriber.d.ts +34 -0
  46. package/lib/events/eventSubscriber.js +126 -0
  47. package/lib/events/fetchLogs.d.ts +13 -0
  48. package/lib/events/fetchLogs.js +39 -0
  49. package/lib/events/pollingLogProvider.d.ts +15 -0
  50. package/lib/events/pollingLogProvider.js +53 -0
  51. package/lib/events/sort.d.ts +2 -0
  52. package/lib/events/sort.js +44 -0
  53. package/lib/events/txEventCache.d.ts +24 -0
  54. package/lib/events/txEventCache.js +71 -0
  55. package/lib/events/types.d.ts +49 -0
  56. package/lib/events/types.js +20 -0
  57. package/lib/events/webSocketLogProvider.d.ts +12 -0
  58. package/lib/events/webSocketLogProvider.js +30 -0
  59. package/lib/examples/makeTradeExample.js +26 -27
  60. package/lib/factory/bigNum.d.ts +118 -0
  61. package/lib/factory/bigNum.js +364 -0
  62. package/lib/factory/oracleClient.d.ts +1 -2
  63. package/lib/factory/oracleClient.js +6 -2
  64. package/lib/idl/clearing_house.json +2287 -2938
  65. package/lib/index.d.ts +12 -4
  66. package/lib/index.js +12 -4
  67. package/lib/math/amm.d.ts +24 -29
  68. package/lib/math/amm.js +239 -178
  69. package/lib/math/auction.d.ts +5 -0
  70. package/lib/math/auction.js +39 -0
  71. package/lib/math/bankBalance.d.ts +9 -0
  72. package/lib/math/bankBalance.js +75 -0
  73. package/lib/math/conversion.d.ts +0 -1
  74. package/lib/math/conversion.js +1 -5
  75. package/lib/math/funding.d.ts +6 -6
  76. package/lib/math/funding.js +158 -175
  77. package/lib/math/market.d.ts +6 -6
  78. package/lib/math/market.js +10 -9
  79. package/lib/math/orders.d.ts +5 -0
  80. package/lib/math/orders.js +31 -1
  81. package/lib/math/position.d.ts +7 -5
  82. package/lib/math/position.js +27 -27
  83. package/lib/math/repeg.d.ts +22 -0
  84. package/lib/math/repeg.js +128 -0
  85. package/lib/math/trade.d.ts +5 -4
  86. package/lib/math/trade.js +29 -21
  87. package/lib/mockUSDCFaucet.js +87 -116
  88. package/lib/oracles/oracleClientCache.d.ts +8 -0
  89. package/lib/oracles/oracleClientCache.js +19 -0
  90. package/lib/oracles/pythClient.d.ts +3 -5
  91. package/lib/oracles/pythClient.js +12 -31
  92. package/lib/oracles/quoteAssetOracleClient.d.ts +9 -0
  93. package/lib/oracles/quoteAssetOracleClient.js +21 -0
  94. package/lib/oracles/switchboardClient.d.ts +3 -5
  95. package/lib/oracles/switchboardClient.js +29 -50
  96. package/lib/oracles/types.d.ts +6 -1
  97. package/lib/orders.d.ts +6 -7
  98. package/lib/orders.js +10 -80
  99. package/lib/slot/SlotSubscriber.d.ts +12 -0
  100. package/lib/slot/SlotSubscriber.js +23 -0
  101. package/lib/tx/retryTxSender.d.ts +2 -2
  102. package/lib/tx/retryTxSender.js +108 -123
  103. package/lib/tx/types.d.ts +5 -1
  104. package/lib/tx/utils.d.ts +1 -1
  105. package/lib/tx/utils.js +11 -2
  106. package/lib/types.d.ts +110 -109
  107. package/lib/types.js +14 -1
  108. package/lib/userName.d.ts +4 -0
  109. package/lib/userName.js +20 -0
  110. package/lib/util/computeUnits.js +10 -21
  111. package/lib/util/tps.js +11 -22
  112. package/lib/wallet.js +7 -20
  113. package/package.json +11 -4
  114. package/src/accounts/bulkAccountLoader.ts +21 -15
  115. package/src/accounts/bulkUserSubscription.ts +1 -45
  116. package/src/accounts/fetch.ts +33 -0
  117. package/src/accounts/pollingClearingHouseAccountSubscriber.ts +249 -214
  118. package/src/accounts/pollingOracleSubscriber.ts +16 -8
  119. package/src/accounts/pollingTokenAccountSubscriber.ts +11 -8
  120. package/src/accounts/pollingUserAccountSubscriber.ts +21 -86
  121. package/src/accounts/types.ts +41 -70
  122. package/src/accounts/webSocketAccountSubscriber.ts +33 -16
  123. package/src/accounts/webSocketClearingHouseAccountSubscriber.ts +190 -240
  124. package/src/accounts/webSocketUserAccountSubscriber.ts +11 -71
  125. package/src/addresses/marketAddresses.ts +18 -0
  126. package/src/addresses/pda.ts +118 -0
  127. package/src/admin.ts +218 -346
  128. package/src/clearingHouse.ts +983 -952
  129. package/src/clearingHouseConfig.ts +37 -0
  130. package/src/clearingHouseUser.ts +275 -185
  131. package/src/clearingHouseUserConfig.ts +18 -0
  132. package/src/config.ts +54 -1
  133. package/src/constants/banks.ts +43 -0
  134. package/src/constants/markets.ts +16 -207
  135. package/src/constants/numericConstants.ts +34 -5
  136. package/src/events/eventList.ts +94 -0
  137. package/src/events/eventSubscriber.ts +194 -0
  138. package/src/events/fetchLogs.ts +80 -0
  139. package/src/events/pollingLogProvider.ts +79 -0
  140. package/src/events/sort.ts +65 -0
  141. package/src/events/txEventCache.ts +74 -0
  142. package/src/events/types.ts +98 -0
  143. package/src/events/webSocketLogProvider.ts +38 -0
  144. package/src/examples/makeTradeExample.ts +20 -11
  145. package/src/factory/bigNum.ts +524 -0
  146. package/src/factory/oracleClient.ts +7 -4
  147. package/src/idl/clearing_house.json +2287 -2938
  148. package/src/index.ts +12 -4
  149. package/src/math/amm.ts +414 -245
  150. package/src/math/auction.ts +39 -0
  151. package/src/math/bankBalance.ts +112 -0
  152. package/src/math/conversion.ts +1 -11
  153. package/src/math/funding.ts +12 -9
  154. package/src/math/market.ts +37 -30
  155. package/src/math/orders.ts +38 -0
  156. package/src/math/position.ts +48 -35
  157. package/src/math/repeg.ts +176 -0
  158. package/src/math/trade.ts +45 -35
  159. package/src/math/utils.js +27 -0
  160. package/src/math/utils.js.map +1 -0
  161. package/src/oracles/oracleClientCache.ts +20 -0
  162. package/src/oracles/pythClient.ts +4 -11
  163. package/src/oracles/quoteAssetOracleClient.ts +25 -0
  164. package/src/oracles/switchboardClient.ts +11 -24
  165. package/src/oracles/types.ts +7 -1
  166. package/src/orders.ts +24 -126
  167. package/src/slot/SlotSubscriber.ts +32 -0
  168. package/src/tx/retryTxSender.ts +6 -4
  169. package/src/tx/types.ts +6 -1
  170. package/src/tx/utils.ts +22 -3
  171. package/src/types.ts +111 -122
  172. package/src/userName.ts +20 -0
  173. package/src/util/computeUnits.js +17 -0
  174. package/src/util/computeUnits.js.map +1 -0
  175. package/tests/bn/test.ts +255 -0
  176. package/tsconfig.json +12 -12
  177. package/lib/addresses.d.ts +0 -10
  178. package/lib/addresses.js +0 -93
  179. package/lib/constants/accounts.d.ts +0 -15
  180. package/lib/constants/accounts.js +0 -18
  181. package/lib/factory/clearingHouse.d.ts +0 -35
  182. package/lib/factory/clearingHouse.js +0 -81
  183. package/lib/factory/clearingHouseUser.d.ts +0 -19
  184. package/lib/factory/clearingHouseUser.js +0 -34
  185. package/lib/math/insuranceFund.d.ts +0 -15
  186. package/lib/math/insuranceFund.js +0 -33
  187. package/lib/settlement.d.ts +0 -4
  188. package/lib/settlement.js +0 -10
  189. package/lib/tx/defaultTxSender.d.ts +0 -8
  190. package/lib/tx/defaultTxSender.js +0 -12
  191. package/src/addresses.ts +0 -82
  192. package/src/constants/accounts.ts +0 -26
  193. package/src/factory/clearingHouse.ts +0 -173
  194. package/src/factory/clearingHouseUser.ts +0 -73
  195. package/src/math/insuranceFund.ts +0 -29
  196. package/src/settlement.ts +0 -9
  197. package/src/tx/defaultTxSender.ts +0 -24
package/src/math/amm.ts CHANGED
@@ -4,50 +4,256 @@ import {
4
4
  MARK_PRICE_PRECISION,
5
5
  PEG_PRECISION,
6
6
  ZERO,
7
- AMM_TO_QUOTE_PRECISION_RATIO,
8
- QUOTE_PRECISION,
9
- AMM_RESERVE_PRECISION,
10
7
  BID_ASK_SPREAD_PRECISION,
11
8
  ONE,
9
+ AMM_TO_QUOTE_PRECISION_RATIO,
10
+ QUOTE_PRECISION,
11
+ MARGIN_PRECISION,
12
+ PRICE_DIV_PEG,
12
13
  } from '../constants/numericConstants';
13
- import { calculateBaseAssetValue } from './position';
14
14
  import {
15
15
  AMM,
16
16
  PositionDirection,
17
17
  SwapDirection,
18
- Market,
18
+ MarketAccount,
19
19
  isVariant,
20
20
  } from '../types';
21
21
  import { assert } from '../assert/assert';
22
+ import { squareRootBN } from '..';
23
+
24
+ import { OraclePriceData } from '../oracles/types';
22
25
  import {
23
- calculatePositionPNL,
24
- calculateMarkPrice,
25
- convertToNumber,
26
- squareRootBN,
27
- } from '..';
26
+ calculateRepegCost,
27
+ calculateAdjustKCost,
28
+ calculateBudgetedPeg,
29
+ } from './repeg';
30
+
31
+ export function calculatePegFromTargetPrice(
32
+ targetPrice: BN,
33
+ baseAssetReserve: BN,
34
+ quoteAssetReserve: BN
35
+ ): BN {
36
+ return targetPrice
37
+ .mul(baseAssetReserve)
38
+ .div(quoteAssetReserve)
39
+ .add(PRICE_DIV_PEG.div(new BN(2)))
40
+ .div(PRICE_DIV_PEG);
41
+ }
42
+
43
+ export function calculateOptimalPegAndBudget(
44
+ amm: AMM,
45
+ oraclePriceData: OraclePriceData
46
+ ): [BN, BN, BN, boolean] {
47
+ const markPriceBefore = calculatePrice(
48
+ amm.baseAssetReserve,
49
+ amm.quoteAssetReserve,
50
+ amm.pegMultiplier
51
+ );
52
+ const targetPrice = oraclePriceData.price;
53
+ const newPeg = calculatePegFromTargetPrice(
54
+ targetPrice,
55
+ amm.baseAssetReserve,
56
+ amm.quoteAssetReserve
57
+ );
58
+ const prePegCost = calculateRepegCost(amm, newPeg);
59
+
60
+ const totalFeeLB = amm.totalExchangeFee.div(new BN(2));
61
+ const budget = BN.max(ZERO, amm.totalFeeMinusDistributions.sub(totalFeeLB));
62
+ if (budget.lt(prePegCost)) {
63
+ const maxPriceSpread = new BN(amm.maxSpread)
64
+ .mul(targetPrice)
65
+ .div(BID_ASK_SPREAD_PRECISION);
66
+
67
+ let newTargetPrice: BN;
68
+ let newOptimalPeg: BN;
69
+ let newBudget: BN;
70
+ const targetPriceGap = markPriceBefore.sub(targetPrice);
71
+ if (targetPriceGap.abs().gt(maxPriceSpread)) {
72
+ const markAdj = targetPriceGap.abs().sub(maxPriceSpread);
73
+
74
+ if (targetPriceGap.lt(new BN(0))) {
75
+ newTargetPrice = markPriceBefore.add(markAdj);
76
+ } else {
77
+ newTargetPrice = markPriceBefore.sub(markAdj);
78
+ }
79
+
80
+ newOptimalPeg = calculatePegFromTargetPrice(
81
+ newTargetPrice,
82
+ amm.baseAssetReserve,
83
+ amm.quoteAssetReserve
84
+ );
85
+
86
+ newBudget = calculateRepegCost(amm, newOptimalPeg);
87
+ return [newTargetPrice, newOptimalPeg, newBudget, false];
88
+ }
89
+ }
90
+
91
+ return [targetPrice, newPeg, budget, true];
92
+ }
93
+
94
+ export function calculateNewAmm(
95
+ amm: AMM,
96
+ oraclePriceData: OraclePriceData
97
+ ): [BN, BN, BN, BN] {
98
+ let pKNumer = new BN(1);
99
+ let pKDenom = new BN(1);
100
+
101
+ const [targetPrice, _newPeg, budget, checkLowerBound] =
102
+ calculateOptimalPegAndBudget(amm, oraclePriceData);
103
+ let prePegCost = calculateRepegCost(amm, _newPeg);
104
+ let newPeg = _newPeg;
105
+
106
+ if (prePegCost.gt(budget) && checkLowerBound) {
107
+ [pKNumer, pKDenom] = [new BN(999), new BN(1000)];
108
+ const deficitMadeup = calculateAdjustKCost(amm, pKNumer, pKDenom);
109
+ assert(deficitMadeup.lte(new BN(0)));
110
+ prePegCost = budget.add(deficitMadeup.abs());
111
+ const newAmm = Object.assign({}, amm);
112
+ newAmm.baseAssetReserve = newAmm.baseAssetReserve.mul(pKNumer).div(pKDenom);
113
+ newAmm.sqrtK = newAmm.sqrtK.mul(pKNumer).div(pKDenom);
114
+ const invariant = newAmm.sqrtK.mul(newAmm.sqrtK);
115
+ newAmm.quoteAssetReserve = invariant.div(newAmm.baseAssetReserve);
116
+ const directionToClose = amm.netBaseAssetAmount.gt(ZERO)
117
+ ? PositionDirection.SHORT
118
+ : PositionDirection.LONG;
119
+
120
+ const [newQuoteAssetReserve, _newBaseAssetReserve] =
121
+ calculateAmmReservesAfterSwap(
122
+ newAmm,
123
+ 'base',
124
+ amm.netBaseAssetAmount.abs(),
125
+ getSwapDirection('base', directionToClose)
126
+ );
127
+
128
+ newAmm.terminalQuoteAssetReserve = newQuoteAssetReserve;
129
+
130
+ newPeg = calculateBudgetedPeg(newAmm, prePegCost, targetPrice);
131
+ prePegCost = calculateRepegCost(newAmm, newPeg);
132
+ }
133
+
134
+ return [prePegCost, pKNumer, pKDenom, newPeg];
135
+ }
136
+
137
+ export function calculateUpdatedAMM(
138
+ amm: AMM,
139
+ oraclePriceData: OraclePriceData
140
+ ): AMM {
141
+ if (amm.curveUpdateIntensity == 0) {
142
+ return amm;
143
+ }
144
+ const newAmm = Object.assign({}, amm);
145
+ const [prepegCost, pKNumer, pKDenom, newPeg] = calculateNewAmm(
146
+ amm,
147
+ oraclePriceData
148
+ );
149
+
150
+ newAmm.baseAssetReserve = newAmm.baseAssetReserve.mul(pKNumer).div(pKDenom);
151
+ newAmm.sqrtK = newAmm.sqrtK.mul(pKNumer).div(pKDenom);
152
+ const invariant = newAmm.sqrtK.mul(newAmm.sqrtK);
153
+ newAmm.quoteAssetReserve = invariant.div(newAmm.baseAssetReserve);
154
+ newAmm.pegMultiplier = newPeg;
155
+
156
+ const directionToClose = amm.netBaseAssetAmount.gt(ZERO)
157
+ ? PositionDirection.SHORT
158
+ : PositionDirection.LONG;
159
+
160
+ const [newQuoteAssetReserve, _newBaseAssetReserve] =
161
+ calculateAmmReservesAfterSwap(
162
+ newAmm,
163
+ 'base',
164
+ amm.netBaseAssetAmount.abs(),
165
+ getSwapDirection('base', directionToClose)
166
+ );
167
+
168
+ newAmm.terminalQuoteAssetReserve = newQuoteAssetReserve;
169
+
170
+ newAmm.totalFeeMinusDistributions =
171
+ newAmm.totalFeeMinusDistributions.sub(prepegCost);
172
+
173
+ return newAmm;
174
+ }
175
+
176
+ export function calculateUpdatedAMMSpreadReserves(
177
+ amm: AMM,
178
+ direction: PositionDirection,
179
+ oraclePriceData: OraclePriceData
180
+ ): { baseAssetReserve: BN; quoteAssetReserve: BN; sqrtK: BN; newPeg: BN } {
181
+ const newAmm = calculateUpdatedAMM(amm, oraclePriceData);
182
+ const dirReserves = calculateSpreadReserves(
183
+ newAmm,
184
+ direction,
185
+ oraclePriceData
186
+ );
187
+ const result = {
188
+ baseAssetReserve: dirReserves.baseAssetReserve,
189
+ quoteAssetReserve: dirReserves.quoteAssetReserve,
190
+ sqrtK: newAmm.sqrtK,
191
+ newPeg: newAmm.pegMultiplier,
192
+ };
193
+
194
+ return result;
195
+ }
196
+
197
+ export function calculateBidAskPrice(
198
+ amm: AMM,
199
+ oraclePriceData: OraclePriceData,
200
+ withUpdate = true
201
+ ): [BN, BN] {
202
+ let newAmm: AMM;
203
+ if (withUpdate) {
204
+ newAmm = calculateUpdatedAMM(amm, oraclePriceData);
205
+ } else {
206
+ newAmm = amm;
207
+ }
208
+
209
+ const askReserves = calculateSpreadReserves(
210
+ newAmm,
211
+ PositionDirection.LONG,
212
+ oraclePriceData
213
+ );
214
+ const bidReserves = calculateSpreadReserves(
215
+ newAmm,
216
+ PositionDirection.SHORT,
217
+ oraclePriceData
218
+ );
219
+
220
+ const askPrice = calculatePrice(
221
+ askReserves.baseAssetReserve,
222
+ askReserves.quoteAssetReserve,
223
+ newAmm.pegMultiplier
224
+ );
225
+
226
+ const bidPrice = calculatePrice(
227
+ bidReserves.baseAssetReserve,
228
+ bidReserves.quoteAssetReserve,
229
+ newAmm.pegMultiplier
230
+ );
231
+
232
+ return [bidPrice, askPrice];
233
+ }
28
234
 
29
235
  /**
30
236
  * Calculates a price given an arbitrary base and quote amount (they must have the same precision)
31
237
  *
32
- * @param baseAssetAmount
33
- * @param quoteAssetAmount
34
- * @param peg_multiplier
238
+ * @param baseAssetReserves
239
+ * @param quoteAssetReserves
240
+ * @param pegMultiplier
35
241
  * @returns price : Precision MARK_PRICE_PRECISION
36
242
  */
37
243
  export function calculatePrice(
38
- baseAssetAmount: BN,
39
- quoteAssetAmount: BN,
40
- peg_multiplier: BN
244
+ baseAssetReserves: BN,
245
+ quoteAssetReserves: BN,
246
+ pegMultiplier: BN
41
247
  ): BN {
42
- if (baseAssetAmount.abs().lte(ZERO)) {
248
+ if (baseAssetReserves.abs().lte(ZERO)) {
43
249
  return new BN(0);
44
250
  }
45
251
 
46
- return quoteAssetAmount
252
+ return quoteAssetReserves
47
253
  .mul(MARK_PRICE_PRECISION)
48
- .mul(peg_multiplier)
254
+ .mul(pegMultiplier)
49
255
  .div(PEG_PRECISION)
50
- .div(baseAssetAmount);
256
+ .div(baseAssetReserves);
51
257
  }
52
258
 
53
259
  export type AssetType = 'quote' | 'base';
@@ -98,17 +304,185 @@ export function calculateAmmReservesAfterSwap(
98
304
  return [newQuoteAssetReserve, newBaseAssetReserve];
99
305
  }
100
306
 
307
+ export function calculateEffectiveLeverage(
308
+ baseSpread: number,
309
+ quoteAssetReserve: BN,
310
+ terminalQuoteAssetReserve: BN,
311
+ pegMultiplier: BN,
312
+ netBaseAssetAmount: BN,
313
+ markPrice: BN,
314
+ totalFeeMinusDistributions: BN
315
+ ): number {
316
+ // inventory skew
317
+ const netBaseAssetValue = quoteAssetReserve
318
+ .sub(terminalQuoteAssetReserve)
319
+ .mul(pegMultiplier)
320
+ .div(AMM_TIMES_PEG_TO_QUOTE_PRECISION_RATIO);
321
+
322
+ const localBaseAssetValue = netBaseAssetAmount
323
+ .mul(markPrice)
324
+ .div(AMM_TO_QUOTE_PRECISION_RATIO.mul(MARK_PRICE_PRECISION));
325
+
326
+ const effectiveLeverage =
327
+ localBaseAssetValue.sub(netBaseAssetValue).toNumber() /
328
+ (Math.max(0, totalFeeMinusDistributions.toNumber()) + 1) +
329
+ 1 / QUOTE_PRECISION.toNumber();
330
+
331
+ return effectiveLeverage;
332
+ }
333
+
334
+ export function calculateMaxSpread(marginRatioInitial: number): number {
335
+ const maxTargetSpread: number = new BN(marginRatioInitial)
336
+ .mul(BID_ASK_SPREAD_PRECISION.div(MARGIN_PRECISION))
337
+ .toNumber();
338
+
339
+ return maxTargetSpread;
340
+ }
341
+
342
+ export function calculateSpreadBN(
343
+ baseSpread: number,
344
+ lastOracleMarkSpreadPct: BN,
345
+ lastOracleConfPct: BN,
346
+ maxSpread: number,
347
+ quoteAssetReserve: BN,
348
+ terminalQuoteAssetReserve: BN,
349
+ pegMultiplier: BN,
350
+ netBaseAssetAmount: BN,
351
+ markPrice: BN,
352
+ totalFeeMinusDistributions: BN
353
+ ): [number, number] {
354
+ let longSpread = baseSpread / 2;
355
+ let shortSpread = baseSpread / 2;
356
+
357
+ if (lastOracleMarkSpreadPct.gt(ZERO)) {
358
+ shortSpread = Math.max(
359
+ shortSpread,
360
+ lastOracleMarkSpreadPct.abs().toNumber() + lastOracleConfPct.toNumber()
361
+ );
362
+ } else if (lastOracleMarkSpreadPct.lt(ZERO)) {
363
+ longSpread = Math.max(
364
+ longSpread,
365
+ lastOracleMarkSpreadPct.abs().toNumber() + lastOracleConfPct.toNumber()
366
+ );
367
+ }
368
+
369
+ const maxTargetSpread: number = maxSpread;
370
+
371
+ const MAX_INVENTORY_SKEW = 5;
372
+
373
+ const effectiveLeverage = calculateEffectiveLeverage(
374
+ baseSpread,
375
+ quoteAssetReserve,
376
+ terminalQuoteAssetReserve,
377
+ pegMultiplier,
378
+ netBaseAssetAmount,
379
+ markPrice,
380
+ totalFeeMinusDistributions
381
+ );
382
+
383
+ if (totalFeeMinusDistributions.gt(ZERO)) {
384
+ const spreadScale = Math.min(MAX_INVENTORY_SKEW, 1 + effectiveLeverage);
385
+ if (netBaseAssetAmount.gt(ZERO)) {
386
+ longSpread *= spreadScale;
387
+ } else {
388
+ shortSpread *= spreadScale;
389
+ }
390
+ } else {
391
+ longSpread *= MAX_INVENTORY_SKEW;
392
+ shortSpread *= MAX_INVENTORY_SKEW;
393
+ }
394
+
395
+ const totalSpread = longSpread + shortSpread;
396
+ if (totalSpread > maxTargetSpread) {
397
+ if (longSpread > shortSpread) {
398
+ longSpread = Math.min(longSpread, maxTargetSpread);
399
+ shortSpread = maxTargetSpread - longSpread;
400
+ } else {
401
+ shortSpread = Math.min(shortSpread, maxTargetSpread);
402
+ longSpread = maxTargetSpread - shortSpread;
403
+ }
404
+ }
405
+
406
+ return [longSpread, shortSpread];
407
+ }
408
+
101
409
  export function calculateSpread(
102
410
  amm: AMM,
103
- direction: PositionDirection
411
+ direction: PositionDirection,
412
+ oraclePriceData: OraclePriceData
104
413
  ): number {
105
- let spread;
414
+ let spread = amm.baseSpread / 2;
106
415
 
107
- // future logic
108
- if (isVariant(direction, 'long')) {
109
- spread = amm.baseSpread;
110
- } else {
111
- spread = amm.baseSpread;
416
+ if (amm.baseSpread == 0 || amm.curveUpdateIntensity == 0) {
417
+ return spread;
418
+ }
419
+
420
+ const markPrice = calculatePrice(
421
+ amm.baseAssetReserve,
422
+ amm.quoteAssetReserve,
423
+ amm.pegMultiplier
424
+ );
425
+
426
+ const targetPrice = oraclePriceData?.price || markPrice;
427
+ const confInterval = oraclePriceData.confidence || ZERO;
428
+
429
+ const targetMarkSpreadPct = markPrice
430
+ .sub(targetPrice)
431
+ .mul(BID_ASK_SPREAD_PRECISION)
432
+ .div(markPrice);
433
+
434
+ const confIntervalPct = confInterval
435
+ .mul(BID_ASK_SPREAD_PRECISION)
436
+ .div(markPrice);
437
+
438
+ // oracle retreat
439
+ if (
440
+ (isVariant(direction, 'long') && targetMarkSpreadPct.lt(ZERO)) ||
441
+ (isVariant(direction, 'short') && targetMarkSpreadPct.gt(ZERO))
442
+ ) {
443
+ spread = Math.max(
444
+ spread,
445
+ targetMarkSpreadPct.abs().toNumber() + confIntervalPct.abs().toNumber()
446
+ );
447
+ }
448
+
449
+ // inventory skew
450
+ const MAX_INVENTORY_SKEW = 5;
451
+ if (
452
+ (amm.netBaseAssetAmount.gt(ZERO) && isVariant(direction, 'long')) ||
453
+ (amm.netBaseAssetAmount.lt(ZERO) && isVariant(direction, 'short')) ||
454
+ amm.totalFeeMinusDistributions.eq(ZERO)
455
+ ) {
456
+ const netCostBasis = amm.quoteAssetAmountLong.sub(
457
+ amm.quoteAssetAmountShort
458
+ );
459
+ const netBaseAssetValue = amm.quoteAssetReserve
460
+ .sub(amm.terminalQuoteAssetReserve)
461
+ .mul(amm.pegMultiplier)
462
+ .div(AMM_TIMES_PEG_TO_QUOTE_PRECISION_RATIO);
463
+
464
+ const localBaseAssetValue = amm.netBaseAssetAmount
465
+ .mul(markPrice)
466
+ .div(AMM_TO_QUOTE_PRECISION_RATIO.mul(MARK_PRICE_PRECISION));
467
+ const netPnl = netBaseAssetValue.sub(netCostBasis);
468
+ const localPnl = localBaseAssetValue.sub(netCostBasis);
469
+
470
+ let effectiveLeverage = MAX_INVENTORY_SKEW;
471
+ if (amm.totalFeeMinusDistributions.gt(ZERO)) {
472
+ effectiveLeverage =
473
+ localPnl.sub(netPnl).toNumber() /
474
+ (amm.totalFeeMinusDistributions.toNumber() + 1);
475
+ }
476
+
477
+ let spreadScale = Math.min(MAX_INVENTORY_SKEW, 1 + effectiveLeverage);
478
+ const maxTargetSpread = BID_ASK_SPREAD_PRECISION.toNumber() / 50; // 2%
479
+ // cap the scale to attempt to only scale up to maxTargetSpread
480
+ // always let the oracle retreat methods go through 100%
481
+ if (spreadScale * spread > maxTargetSpread) {
482
+ spreadScale = Math.max(1.05, maxTargetSpread / spread);
483
+ }
484
+
485
+ spread *= spreadScale;
112
486
  }
113
487
 
114
488
  return spread;
@@ -116,12 +490,13 @@ export function calculateSpread(
116
490
 
117
491
  export function calculateSpreadReserves(
118
492
  amm: AMM,
119
- direction: PositionDirection
493
+ direction: PositionDirection,
494
+ oraclePriceData: OraclePriceData
120
495
  ): {
121
496
  baseAssetReserve: BN;
122
497
  quoteAssetReserve: BN;
123
498
  } {
124
- const spread = calculateSpread(amm, direction);
499
+ const spread = calculateSpread(amm, direction, oraclePriceData);
125
500
 
126
501
  if (spread === 0) {
127
502
  return {
@@ -131,7 +506,7 @@ export function calculateSpreadReserves(
131
506
  }
132
507
 
133
508
  const quoteAsserReserveDelta = amm.quoteAssetReserve.div(
134
- BID_ASK_SPREAD_PRECISION.div(new BN(spread / 4))
509
+ BID_ASK_SPREAD_PRECISION.div(new BN(spread / 2))
135
510
  );
136
511
 
137
512
  let quoteAssetReserve;
@@ -194,133 +569,14 @@ export function getSwapDirection(
194
569
  return SwapDirection.ADD;
195
570
  }
196
571
 
197
- /**
198
- * Helper function calculating adjust k cost
199
- * @param market
200
- * @param marketIndex
201
- * @param numerator
202
- * @param denomenator
203
- * @returns cost : Precision QUOTE_ASSET_PRECISION
204
- */
205
- export function calculateAdjustKCost(
206
- market: Market,
207
- marketIndex: BN,
208
- numerator: BN,
209
- denomenator: BN
210
- ): BN {
211
- const netUserPosition = {
212
- baseAssetAmount: market.baseAssetAmount,
213
- lastCumulativeFundingRate: market.amm.cumulativeFundingRate,
214
- marketIndex: new BN(marketIndex),
215
- quoteAssetAmount: new BN(0),
216
- openOrders: new BN(0),
217
- };
218
-
219
- const currentValue = calculateBaseAssetValue(market, netUserPosition);
220
-
221
- const marketNewK = Object.assign({}, market);
222
- marketNewK.amm = Object.assign({}, market.amm);
223
-
224
- marketNewK.amm.baseAssetReserve = market.amm.baseAssetReserve
225
- .mul(numerator)
226
- .div(denomenator);
227
- marketNewK.amm.quoteAssetReserve = market.amm.quoteAssetReserve
228
- .mul(numerator)
229
- .div(denomenator);
230
- marketNewK.amm.sqrtK = market.amm.sqrtK.mul(numerator).div(denomenator);
231
-
232
- netUserPosition.quoteAssetAmount = currentValue;
233
-
234
- const cost = calculatePositionPNL(marketNewK, netUserPosition);
235
-
236
- const p = PEG_PRECISION.mul(numerator).div(denomenator);
237
- const x = market.amm.baseAssetReserve;
238
- const y = market.amm.quoteAssetReserve;
239
- const delta = market.baseAssetAmount;
240
- const k = market.amm.sqrtK.mul(market.amm.sqrtK);
241
-
242
- const numer1 = PEG_PRECISION.sub(p).mul(y).div(PEG_PRECISION);
243
- const numer20 = k
244
- .mul(p)
245
- .mul(p)
246
- .div(PEG_PRECISION)
247
- .div(PEG_PRECISION)
248
- .div(x.mul(p).div(PEG_PRECISION).add(delta));
249
- const numer21 = k.div(x.add(delta));
250
-
251
- const formulaCost = numer21
252
- .sub(numer20)
253
- .sub(numer1)
254
- .mul(market.amm.pegMultiplier)
255
- .div(AMM_TIMES_PEG_TO_QUOTE_PRECISION_RATIO);
256
- console.log(convertToNumber(formulaCost, QUOTE_PRECISION));
257
-
258
- // p.div(p.mul(x).add(delta)).sub()
259
-
260
- return cost;
261
- }
262
-
263
- /**
264
- * Helper function calculating adjust pegMultiplier (repeg) cost
265
- *
266
- * @param market
267
- * @param marketIndex
268
- * @param newPeg
269
- * @returns cost : Precision QUOTE_ASSET_PRECISION
270
- */
271
- export function calculateRepegCost(
272
- market: Market,
273
- marketIndex: BN,
274
- newPeg: BN
275
- ): BN {
276
- const netUserPosition = {
277
- baseAssetAmount: market.baseAssetAmount,
278
- lastCumulativeFundingRate: market.amm.cumulativeFundingRate,
279
- marketIndex: new BN(marketIndex),
280
- quoteAssetAmount: new BN(0),
281
- openOrders: new BN(0),
282
- };
283
-
284
- const currentValue = calculateBaseAssetValue(market, netUserPosition);
285
- netUserPosition.quoteAssetAmount = currentValue;
286
- const prevMarketPrice = calculateMarkPrice(market);
287
- const marketNewPeg = Object.assign({}, market);
288
- marketNewPeg.amm = Object.assign({}, market.amm);
289
-
290
- // const marketNewPeg = JSON.parse(JSON.stringify(market));
291
- marketNewPeg.amm.pegMultiplier = newPeg;
292
-
293
- console.log(
294
- 'Price moves from',
295
- convertToNumber(prevMarketPrice),
296
- 'to',
297
- convertToNumber(calculateMarkPrice(marketNewPeg))
298
- );
299
-
300
- const cost = calculatePositionPNL(marketNewPeg, netUserPosition);
301
-
302
- const k = market.amm.sqrtK.mul(market.amm.sqrtK);
303
- const newQuoteAssetReserve = k.div(
304
- market.amm.baseAssetReserve.add(netUserPosition.baseAssetAmount)
305
- );
306
- const deltaQuoteAssetReserves = newQuoteAssetReserve.sub(
307
- market.amm.quoteAssetReserve
308
- );
309
- const cost2 = deltaQuoteAssetReserves
310
- .mul(market.amm.pegMultiplier.sub(newPeg))
311
- .div(AMM_TIMES_PEG_TO_QUOTE_PRECISION_RATIO);
312
- console.log(convertToNumber(cost2, QUOTE_PRECISION));
313
- return cost;
314
- }
315
-
316
572
  /**
317
573
  * Helper function calculating terminal price of amm
318
574
  *
319
575
  * @param market
320
576
  * @returns cost : Precision MARK_PRICE_PRECISION
321
577
  */
322
- export function calculateTerminalPrice(market: Market) {
323
- const directionToClose = market.baseAssetAmount.gt(ZERO)
578
+ export function calculateTerminalPrice(market: MarketAccount) {
579
+ const directionToClose = market.amm.netBaseAssetAmount.gt(ZERO)
324
580
  ? PositionDirection.SHORT
325
581
  : PositionDirection.LONG;
326
582
 
@@ -328,7 +584,7 @@ export function calculateTerminalPrice(market: Market) {
328
584
  calculateAmmReservesAfterSwap(
329
585
  market.amm,
330
586
  'base',
331
- market.baseAssetAmount.abs(),
587
+ market.amm.netBaseAssetAmount.abs(),
332
588
  getSwapDirection('base', directionToClose)
333
589
  );
334
590
 
@@ -345,7 +601,7 @@ export function calculateMaxBaseAssetAmountToTrade(
345
601
  amm: AMM,
346
602
  limit_price: BN,
347
603
  direction: PositionDirection,
348
- useSpread: boolean
604
+ oraclePriceData?: OraclePriceData
349
605
  ): [BN, PositionDirection] {
350
606
  const invariant = amm.sqrtK.mul(amm.sqrtK);
351
607
 
@@ -357,15 +613,11 @@ export function calculateMaxBaseAssetAmountToTrade(
357
613
 
358
614
  const newBaseAssetReserve = squareRootBN(newBaseAssetReserveSquared);
359
615
 
360
- let baseAssetReserveBefore;
361
- if (useSpread) {
362
- baseAssetReserveBefore = calculateSpreadReserves(
363
- amm,
364
- direction
365
- ).baseAssetReserve;
366
- } else {
367
- baseAssetReserveBefore = amm.baseAssetReserve;
368
- }
616
+ const baseAssetReserveBefore = calculateSpreadReserves(
617
+ amm,
618
+ direction,
619
+ oraclePriceData
620
+ ).baseAssetReserve;
369
621
 
370
622
  if (newBaseAssetReserve.gt(baseAssetReserveBefore)) {
371
623
  return [
@@ -383,89 +635,6 @@ export function calculateMaxBaseAssetAmountToTrade(
383
635
  }
384
636
  }
385
637
 
386
- export function calculateBudgetedK(market: Market, cost: BN): [BN, BN] {
387
- // wolframalpha.com
388
- // (1/(x+d) - p/(x*p+d))*y*d*Q = C solve for p
389
- // p = (d(y*d*Q - C(x+d))) / (C*x(x+d) + y*y*d*Q)
390
-
391
- // todo: assumes k = x * y
392
- // otherwise use: (y(1-p) + (kp^2/(x*p+d)) - k/(x+d)) * Q = C solve for p
393
-
394
- // const k = market.amm.sqrtK.mul(market.amm.sqrtK);
395
- const x = market.amm.baseAssetReserve;
396
- const y = market.amm.quoteAssetReserve;
397
-
398
- const d = market.baseAssetAmount;
399
- const Q = market.amm.pegMultiplier;
400
-
401
- const C = cost.mul(new BN(-1));
402
-
403
- const numer1 = y.mul(d).mul(Q).div(AMM_RESERVE_PRECISION).div(PEG_PRECISION);
404
- const numer2 = C.mul(x.add(d)).div(QUOTE_PRECISION);
405
- const denom1 = C.mul(x)
406
- .mul(x.add(d))
407
- .div(AMM_RESERVE_PRECISION)
408
- .div(QUOTE_PRECISION);
409
- const denom2 = y
410
- .mul(d)
411
- .mul(d)
412
- .mul(Q)
413
- .div(AMM_RESERVE_PRECISION)
414
- .div(AMM_RESERVE_PRECISION)
415
- .div(PEG_PRECISION);
416
-
417
- const numerator = d
418
- .mul(numer1.add(numer2))
419
- .div(AMM_RESERVE_PRECISION)
420
- .div(AMM_RESERVE_PRECISION)
421
- .div(AMM_TO_QUOTE_PRECISION_RATIO);
422
- const denominator = denom1
423
- .add(denom2)
424
- .div(AMM_RESERVE_PRECISION)
425
- .div(AMM_TO_QUOTE_PRECISION_RATIO);
426
- console.log(numerator, denominator);
427
- // const p = (numerator).div(denominator);
428
-
429
- // const formulaCost = (numer21.sub(numer20).sub(numer1)).mul(market.amm.pegMultiplier).div(AMM_TIMES_PEG_TO_QUOTE_PRECISION_RATIO)
430
- // console.log(convertToNumber(formulaCost, QUOTE_PRECISION))
431
-
432
- return [numerator, denominator];
433
- }
434
-
435
- export function calculateBudgetedPeg(market: Market, cost: BN): BN {
436
- // wolframalpha.com
437
- // (1/(x+d) - p/(x*p+d))*y*d*Q = C solve for p
438
- // p = (d(y*d*Q - C(x+d))) / (C*x(x+d) + y*y*d*Q)
439
-
440
- // todo: assumes k = x * y
441
- // otherwise use: (y(1-p) + (kp^2/(x*p+d)) - k/(x+d)) * Q = C solve for p
442
-
443
- const k = market.amm.sqrtK.mul(market.amm.sqrtK);
444
- const x = market.amm.baseAssetReserve;
445
- const y = market.amm.quoteAssetReserve;
446
-
447
- const d = market.baseAssetAmount;
448
- const Q = market.amm.pegMultiplier;
449
-
450
- const C = cost.mul(new BN(-1));
451
-
452
- const deltaQuoteAssetReserves = y.sub(k.div(x.add(d)));
453
- const deltaPegMultiplier = C.mul(MARK_PRICE_PRECISION)
454
- .div(deltaQuoteAssetReserves.div(AMM_TO_QUOTE_PRECISION_RATIO))
455
- .mul(PEG_PRECISION)
456
- .div(QUOTE_PRECISION);
457
- console.log(
458
- Q.toNumber(),
459
- 'change by',
460
- deltaPegMultiplier.toNumber() / MARK_PRICE_PRECISION.toNumber()
461
- );
462
- const newPeg = Q.sub(
463
- deltaPegMultiplier.mul(PEG_PRECISION).div(MARK_PRICE_PRECISION)
464
- );
465
-
466
- return newPeg;
467
- }
468
-
469
638
  export function calculateQuoteAssetAmountSwapped(
470
639
  quoteAssetReserves: BN,
471
640
  pegMultiplier: BN,