@drift-labs/sdk 0.1.18-master.0 → 0.1.18-orders.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (108) hide show
  1. package/lib/accounts/defaultClearingHouseAccountSubscriber.d.ts +7 -4
  2. package/lib/accounts/defaultClearingHouseAccountSubscriber.js +27 -2
  3. package/lib/accounts/defaultHistoryAccountSubscriber.d.ts +28 -0
  4. package/lib/accounts/defaultHistoryAccountSubscriber.js +110 -0
  5. package/lib/accounts/defaultUserAccountSubscriber.d.ts +3 -2
  6. package/lib/accounts/defaultUserAccountSubscriber.js +12 -1
  7. package/lib/accounts/types.d.ts +31 -5
  8. package/lib/accounts/webSocketAccountSubscriber.d.ts +0 -1
  9. package/lib/addresses.d.ts +4 -1
  10. package/lib/addresses.js +28 -1
  11. package/lib/admin.d.ts +10 -5
  12. package/lib/admin.js +53 -30
  13. package/lib/assert/assert.d.ts +0 -1
  14. package/lib/clearingHouse.d.ts +22 -3
  15. package/lib/clearingHouse.js +246 -15
  16. package/lib/clearingHouseUser.d.ts +10 -17
  17. package/lib/clearingHouseUser.js +111 -97
  18. package/lib/config.d.ts +0 -1
  19. package/lib/config.js +1 -1
  20. package/lib/constants/markets.d.ts +0 -1
  21. package/lib/constants/numericConstants.d.ts +0 -1
  22. package/lib/examples/makeTradeExample.d.ts +0 -1
  23. package/lib/examples/makeTradeExample.js +6 -6
  24. package/lib/idl/clearing_house.json +895 -132
  25. package/lib/index.d.ts +4 -1
  26. package/lib/index.js +4 -0
  27. package/lib/math/amm.d.ts +1 -1
  28. package/lib/math/amm.js +38 -15
  29. package/lib/math/conversion.d.ts +0 -1
  30. package/lib/math/conversion.js +1 -1
  31. package/lib/math/funding.d.ts +0 -1
  32. package/lib/math/funding.js +1 -1
  33. package/lib/math/insuranceFund.d.ts +0 -1
  34. package/lib/math/market.d.ts +2 -2
  35. package/lib/math/market.js +12 -2
  36. package/lib/math/orders.d.ts +3 -0
  37. package/lib/math/orders.js +30 -0
  38. package/lib/math/position.d.ts +4 -2
  39. package/lib/math/position.js +19 -5
  40. package/lib/math/trade.d.ts +0 -1
  41. package/lib/math/trade.js +16 -16
  42. package/lib/math/utils.d.ts +0 -1
  43. package/lib/mockUSDCFaucet.d.ts +0 -1
  44. package/lib/orderParams.d.ts +7 -0
  45. package/lib/orderParams.js +88 -0
  46. package/lib/orders.d.ts +5 -0
  47. package/lib/orders.js +136 -0
  48. package/lib/pythClient.d.ts +0 -1
  49. package/lib/pythClient.js +1 -1
  50. package/lib/tx/defaultTxSender.d.ts +0 -1
  51. package/lib/tx/types.d.ts +0 -1
  52. package/lib/tx/utils.d.ts +0 -1
  53. package/lib/types.d.ts +140 -7
  54. package/lib/types.js +36 -1
  55. package/lib/util/computeUnits.d.ts +0 -1
  56. package/lib/util/tps.d.ts +0 -1
  57. package/lib/wallet.d.ts +0 -1
  58. package/package.json +1 -1
  59. package/src/accounts/defaultClearingHouseAccountSubscriber.ts +52 -5
  60. package/src/accounts/defaultHistoryAccountSubscriber.ts +176 -0
  61. package/src/accounts/defaultUserAccountSubscriber.ts +29 -2
  62. package/src/accounts/types.ts +41 -4
  63. package/src/addresses.ts +35 -0
  64. package/src/admin.ts +86 -33
  65. package/src/clearingHouse.ts +340 -7
  66. package/src/clearingHouseUser.ts +154 -102
  67. package/src/config.ts +1 -1
  68. package/src/idl/clearing_house.json +895 -132
  69. package/src/index.ts +4 -0
  70. package/src/math/amm.ts +47 -14
  71. package/src/math/market.ts +28 -2
  72. package/src/math/orders.ts +39 -0
  73. package/src/math/position.ts +23 -3
  74. package/src/orderParams.ts +128 -0
  75. package/src/orders.ts +230 -0
  76. package/src/types.ts +124 -6
  77. package/tsconfig.json +0 -1
  78. package/lib/accounts/defaultClearingHouseAccountSubscriber.d.ts.map +0 -1
  79. package/lib/accounts/defaultUserAccountSubscriber.d.ts.map +0 -1
  80. package/lib/accounts/types.d.ts.map +0 -1
  81. package/lib/accounts/webSocketAccountSubscriber.d.ts.map +0 -1
  82. package/lib/addresses.d.ts.map +0 -1
  83. package/lib/admin.d.ts.map +0 -1
  84. package/lib/assert/assert.d.ts.map +0 -1
  85. package/lib/clearingHouse.d.ts.map +0 -1
  86. package/lib/clearingHouseUser.d.ts.map +0 -1
  87. package/lib/config.d.ts.map +0 -1
  88. package/lib/constants/markets.d.ts.map +0 -1
  89. package/lib/constants/numericConstants.d.ts.map +0 -1
  90. package/lib/examples/makeTradeExample.d.ts.map +0 -1
  91. package/lib/index.d.ts.map +0 -1
  92. package/lib/math/amm.d.ts.map +0 -1
  93. package/lib/math/conversion.d.ts.map +0 -1
  94. package/lib/math/funding.d.ts.map +0 -1
  95. package/lib/math/insuranceFund.d.ts.map +0 -1
  96. package/lib/math/market.d.ts.map +0 -1
  97. package/lib/math/position.d.ts.map +0 -1
  98. package/lib/math/trade.d.ts.map +0 -1
  99. package/lib/math/utils.d.ts.map +0 -1
  100. package/lib/mockUSDCFaucet.d.ts.map +0 -1
  101. package/lib/pythClient.d.ts.map +0 -1
  102. package/lib/tx/defaultTxSender.d.ts.map +0 -1
  103. package/lib/tx/types.d.ts.map +0 -1
  104. package/lib/tx/utils.d.ts.map +0 -1
  105. package/lib/types.d.ts.map +0 -1
  106. package/lib/util/computeUnits.d.ts.map +0 -1
  107. package/lib/util/tps.d.ts.map +0 -1
  108. package/lib/wallet.d.ts.map +0 -1
package/src/index.ts CHANGED
@@ -4,6 +4,7 @@ export * from './mockUSDCFaucet';
4
4
  export * from './pythClient';
5
5
  export * from './types';
6
6
  export * from './constants/markets';
7
+ export * from './accounts/defaultHistoryAccountSubscriber';
7
8
  export * from './accounts/defaultClearingHouseAccountSubscriber';
8
9
  export * from './accounts/types';
9
10
  export * from './addresses';
@@ -17,6 +18,9 @@ export * from './math/market';
17
18
  export * from './math/position';
18
19
  export * from './math/amm';
19
20
  export * from './math/trade';
21
+ export * from './math/orders';
22
+ export * from './orders';
23
+ export * from './orderParams';
20
24
  export * from './wallet';
21
25
  export * from './types';
22
26
  export * from './math/utils';
package/src/math/amm.ts CHANGED
@@ -6,9 +6,20 @@ import {
6
6
  ZERO,
7
7
  } from '../constants/numericConstants';
8
8
  import { calculateBaseAssetValue } from './position';
9
- import { AMM, PositionDirection, SwapDirection, Market } from '../types';
9
+ import {
10
+ AMM,
11
+ PositionDirection,
12
+ SwapDirection,
13
+ Market,
14
+ isVariant,
15
+ } from '../types';
10
16
  import { assert } from '../assert/assert';
11
- import { calculatePositionPNL, calculateMarkPrice, convertToNumber } from '..';
17
+ import {
18
+ calculatePositionPNL,
19
+ calculateMarkPrice,
20
+ convertToNumber,
21
+ squareRootBN,
22
+ } from '..';
12
23
 
13
24
  /**
14
25
  * Calculates a price given an arbitrary base and quote amount (they must have the same precision)
@@ -114,17 +125,11 @@ export function getSwapDirection(
114
125
  inputAssetType: AssetType,
115
126
  positionDirection: PositionDirection
116
127
  ): SwapDirection {
117
- if (
118
- positionDirection === PositionDirection.LONG &&
119
- inputAssetType === 'base'
120
- ) {
128
+ if (isVariant(positionDirection, 'long') && inputAssetType === 'base') {
121
129
  return SwapDirection.REMOVE;
122
130
  }
123
131
 
124
- if (
125
- positionDirection === PositionDirection.SHORT &&
126
- inputAssetType === 'quote'
127
- ) {
132
+ if (isVariant(positionDirection, 'short') && inputAssetType === 'quote') {
128
133
  return SwapDirection.REMOVE;
129
134
  }
130
135
 
@@ -150,6 +155,7 @@ export function calculateAdjustKCost(
150
155
  lastCumulativeFundingRate: market.amm.cumulativeFundingRate,
151
156
  marketIndex: new BN(marketIndex),
152
157
  quoteAssetAmount: new BN(0),
158
+ openOrders: new BN(0),
153
159
  };
154
160
 
155
161
  const currentValue = calculateBaseAssetValue(market, netUserPosition);
@@ -190,6 +196,7 @@ export function calculateRepegCost(
190
196
  lastCumulativeFundingRate: market.amm.cumulativeFundingRate,
191
197
  marketIndex: new BN(marketIndex),
192
198
  quoteAssetAmount: new BN(0),
199
+ openOrders: new BN(0),
193
200
  };
194
201
 
195
202
  const currentValue = calculateBaseAssetValue(market, netUserPosition);
@@ -220,10 +227,6 @@ export function calculateRepegCost(
220
227
  * @returns cost : Precision MARK_PRICE_PRECISION
221
228
  */
222
229
  export function calculateTerminalPrice(market: Market) {
223
- if (!market.initialized) {
224
- return new BN(0);
225
- }
226
-
227
230
  const directionToClose = market.baseAssetAmount.gt(ZERO)
228
231
  ? PositionDirection.SHORT
229
232
  : PositionDirection.LONG;
@@ -243,3 +246,33 @@ export function calculateTerminalPrice(market: Market) {
243
246
 
244
247
  return terminalPrice;
245
248
  }
249
+
250
+ export function calculateMaxBaseAssetAmountToTrade(
251
+ amm: AMM,
252
+ limit_price: BN
253
+ ): [BN, PositionDirection] {
254
+ const invariant = amm.sqrtK.mul(amm.sqrtK);
255
+
256
+ const newBaseAssetReserveSquared = invariant
257
+ .mul(MARK_PRICE_PRECISION)
258
+ .mul(amm.pegMultiplier)
259
+ .div(limit_price)
260
+ .div(PEG_PRECISION);
261
+
262
+ const newBaseAssetReserve = squareRootBN(newBaseAssetReserveSquared);
263
+
264
+ if (newBaseAssetReserve.gt(amm.baseAssetReserve)) {
265
+ return [
266
+ newBaseAssetReserve.sub(amm.baseAssetReserve),
267
+ PositionDirection.SHORT,
268
+ ];
269
+ } else if (newBaseAssetReserve.lt(amm.baseAssetReserve)) {
270
+ return [
271
+ amm.baseAssetReserve.sub(newBaseAssetReserve),
272
+ PositionDirection.LONG,
273
+ ];
274
+ } else {
275
+ console.log('tradeSize Too Small');
276
+ return [new BN(0), PositionDirection.LONG];
277
+ }
278
+ }
@@ -1,6 +1,10 @@
1
1
  import { BN } from '@project-serum/anchor';
2
- import { Market } from '../types';
3
- import { calculatePrice } from './amm';
2
+ import { Market, PositionDirection } from '../types';
3
+ import {
4
+ calculateAmmReservesAfterSwap,
5
+ calculatePrice,
6
+ getSwapDirection,
7
+ } from './amm';
4
8
 
5
9
  /**
6
10
  * Calculates market mark price
@@ -15,3 +19,25 @@ export function calculateMarkPrice(market: Market): BN {
15
19
  market.amm.pegMultiplier
16
20
  );
17
21
  }
22
+
23
+ export function calculateNewMarketAfterTrade(
24
+ baseAssetAmount: BN,
25
+ direction: PositionDirection,
26
+ market: Market
27
+ ): Market {
28
+ const [newQuoteAssetReserve, newBaseAssetReserve] =
29
+ calculateAmmReservesAfterSwap(
30
+ market.amm,
31
+ 'base',
32
+ baseAssetAmount.abs(),
33
+ getSwapDirection('base', direction)
34
+ );
35
+
36
+ const newAmm = Object.assign({}, market.amm);
37
+ const newMarket = Object.assign({}, market);
38
+ newMarket.amm = newAmm;
39
+ newMarket.amm.quoteAssetReserve = newQuoteAssetReserve;
40
+ newMarket.amm.baseAssetReserve = newBaseAssetReserve;
41
+
42
+ return newMarket;
43
+ }
@@ -0,0 +1,39 @@
1
+ import { ClearingHouseUser } from '../clearingHouseUser';
2
+ import { isVariant, Order } from '../types';
3
+ import { ZERO } from '../constants/numericConstants';
4
+
5
+ export function isOrderRiskIncreasing(
6
+ user: ClearingHouseUser,
7
+ order: Order
8
+ ): boolean {
9
+ if (isVariant(order.status, 'init')) {
10
+ return false;
11
+ }
12
+
13
+ const position =
14
+ user.getUserPosition(order.marketIndex) ||
15
+ user.getEmptyPosition(order.marketIndex);
16
+
17
+ // if no position exists, it's risk increasing
18
+ if (position.baseAssetAmount.eq(ZERO)) {
19
+ return true;
20
+ }
21
+
22
+ // if position is long and order is long
23
+ if (position.baseAssetAmount.gt(ZERO) && isVariant(order.direction, 'long')) {
24
+ return true;
25
+ }
26
+
27
+ // if position is short and order is short
28
+ if (
29
+ position.baseAssetAmount.lt(ZERO) &&
30
+ isVariant(order.direction, 'short')
31
+ ) {
32
+ return true;
33
+ }
34
+
35
+ // if order will flip position
36
+ if (position.baseAssetAmount.abs().gt(order.baseAssetAmountFilled)) {
37
+ return true;
38
+ }
39
+ }
@@ -27,9 +27,7 @@ export function calculateBaseAssetValue(
27
27
  return ZERO;
28
28
  }
29
29
 
30
- const directionToClose = userPosition.baseAssetAmount.gt(ZERO)
31
- ? PositionDirection.SHORT
32
- : PositionDirection.LONG;
30
+ const directionToClose = findDirectionToClose(userPosition);
33
31
 
34
32
  const [newQuoteAssetReserve, _] = calculateAmmReservesAfterSwap(
35
33
  market.amm,
@@ -138,3 +136,25 @@ export function calculateEntryPrice(userPosition: UserPosition): BN {
138
136
  .div(userPosition.baseAssetAmount)
139
137
  .abs();
140
138
  }
139
+
140
+ export function findDirectionToClose(
141
+ userPosition: UserPosition
142
+ ): PositionDirection {
143
+ return userPosition.baseAssetAmount.gt(ZERO)
144
+ ? PositionDirection.SHORT
145
+ : PositionDirection.LONG;
146
+ }
147
+
148
+ export function positionCurrentDirection(
149
+ userPosition: UserPosition
150
+ ): PositionDirection {
151
+ return userPosition.baseAssetAmount.gte(ZERO)
152
+ ? PositionDirection.LONG
153
+ : PositionDirection.SHORT;
154
+ }
155
+
156
+ export function isEmptyPosition(userPosition: UserPosition): boolean {
157
+ return (
158
+ userPosition.baseAssetAmount.eq(ZERO) && userPosition.openOrders.eq(ZERO)
159
+ );
160
+ }
@@ -0,0 +1,128 @@
1
+ import {
2
+ OrderParams,
3
+ OrderTriggerCondition,
4
+ OrderType,
5
+ PositionDirection,
6
+ } from './types';
7
+ import { BN } from '@project-serum/anchor';
8
+ import { ZERO } from './constants/numericConstants';
9
+
10
+ export function getLimitOrderParams(
11
+ marketIndex: BN,
12
+ direction: PositionDirection,
13
+ baseAssetAmount: BN,
14
+ price: BN,
15
+ reduceOnly: boolean,
16
+ discountToken = false,
17
+ referrer = false
18
+ ): OrderParams {
19
+ return {
20
+ orderType: OrderType.LIMIT,
21
+ marketIndex,
22
+ direction,
23
+ quoteAssetAmount: ZERO,
24
+ baseAssetAmount,
25
+ price,
26
+ reduceOnly,
27
+ postOnly: false,
28
+ immediateOrCancel: false,
29
+ optionalAccounts: {
30
+ discountToken,
31
+ referrer,
32
+ },
33
+ triggerCondition: OrderTriggerCondition.ABOVE,
34
+ triggerPrice: ZERO,
35
+ };
36
+ }
37
+
38
+ export function getStopOrderParams(
39
+ marketIndex: BN,
40
+ direction: PositionDirection,
41
+ baseAssetAmount: BN,
42
+ triggerPrice: BN,
43
+ triggerCondition: OrderTriggerCondition,
44
+ reduceOnly: boolean,
45
+ discountToken = false,
46
+ referrer = false
47
+ ): OrderParams {
48
+ return {
49
+ orderType: OrderType.STOP,
50
+ marketIndex,
51
+ direction,
52
+ quoteAssetAmount: ZERO,
53
+ baseAssetAmount,
54
+ price: ZERO,
55
+ reduceOnly,
56
+ postOnly: false,
57
+ immediateOrCancel: false,
58
+ optionalAccounts: {
59
+ discountToken,
60
+ referrer,
61
+ },
62
+ triggerCondition,
63
+ triggerPrice,
64
+ };
65
+ }
66
+
67
+ export function getStopLimitOrderParams(
68
+ marketIndex: BN,
69
+ direction: PositionDirection,
70
+ baseAssetAmount: BN,
71
+ price: BN,
72
+ triggerPrice: BN,
73
+ triggerCondition: OrderTriggerCondition,
74
+ reduceOnly: boolean,
75
+ discountToken = false,
76
+ referrer = false
77
+ ): OrderParams {
78
+ return {
79
+ orderType: OrderType.STOP_LIMIT,
80
+ marketIndex,
81
+ direction,
82
+ quoteAssetAmount: ZERO,
83
+ baseAssetAmount,
84
+ price,
85
+ reduceOnly,
86
+ postOnly: false,
87
+ immediateOrCancel: false,
88
+ optionalAccounts: {
89
+ discountToken,
90
+ referrer,
91
+ },
92
+ triggerCondition,
93
+ triggerPrice,
94
+ };
95
+ }
96
+
97
+ export function getMarketOrderParams(
98
+ marketIndex: BN,
99
+ direction: PositionDirection,
100
+ quoteAssetAmount: BN,
101
+ baseAssetAmount: BN,
102
+ reduceOnly: boolean,
103
+ price = ZERO,
104
+ discountToken = false,
105
+ referrer = false
106
+ ): OrderParams {
107
+ if (baseAssetAmount.eq(ZERO) && quoteAssetAmount.eq(ZERO)) {
108
+ throw Error('baseAssetAmount or quoteAssetAmount must be zero');
109
+ }
110
+
111
+ return {
112
+ orderType: OrderType.MARKET,
113
+ marketIndex,
114
+ direction,
115
+ quoteAssetAmount,
116
+ baseAssetAmount,
117
+ price,
118
+ reduceOnly,
119
+ postOnly: false,
120
+ immediateOrCancel: false,
121
+ optionalAccounts: {
122
+ discountToken,
123
+ referrer,
124
+ },
125
+ triggerCondition: OrderTriggerCondition.ABOVE,
126
+ triggerPrice: ZERO,
127
+ };
128
+ }
package/src/orders.ts ADDED
@@ -0,0 +1,230 @@
1
+ import {
2
+ isVariant,
3
+ Market,
4
+ Order,
5
+ PositionDirection,
6
+ UserAccount,
7
+ UserPosition,
8
+ } from './types';
9
+ import BN from 'bn.js';
10
+ import {
11
+ calculateMarkPrice,
12
+ calculateNewMarketAfterTrade,
13
+ } from './math/market';
14
+ import {
15
+ AMM_TO_QUOTE_PRECISION_RATIO,
16
+ PEG_PRECISION,
17
+ ZERO,
18
+ } from './constants/numericConstants';
19
+ import { calculateMaxBaseAssetAmountToTrade } from './math/amm';
20
+ import {
21
+ findDirectionToClose,
22
+ positionCurrentDirection,
23
+ } from './math/position';
24
+
25
+ export function calculateNewStateAfterOrder(
26
+ userAccount: UserAccount,
27
+ userPosition: UserPosition,
28
+ market: Market,
29
+ order: Order
30
+ ): [UserAccount, UserPosition, Market] | null {
31
+ if (isVariant(order.status, 'init')) {
32
+ return null;
33
+ }
34
+
35
+ const baseAssetAmountToTrade = calculateAmountToTrade(market, order);
36
+ if (baseAssetAmountToTrade.lt(market.amm.minimumBaseAssetTradeSize)) {
37
+ return null;
38
+ }
39
+
40
+ const userAccountAfter = Object.assign({}, userAccount);
41
+ const userPositionAfter = Object.assign({}, userPosition);
42
+
43
+ const currentPositionDirection = positionCurrentDirection(userPosition);
44
+ const increasePosition =
45
+ userPosition.baseAssetAmount.eq(ZERO) ||
46
+ isSameDirection(order.direction, currentPositionDirection);
47
+
48
+ if (increasePosition) {
49
+ const marketAfter = calculateNewMarketAfterTrade(
50
+ baseAssetAmountToTrade,
51
+ order.direction,
52
+ market
53
+ );
54
+
55
+ const { quoteAssetAmountSwapped, baseAssetAmountSwapped } =
56
+ calculateAmountSwapped(market, marketAfter);
57
+
58
+ userPositionAfter.baseAssetAmount = userPositionAfter.baseAssetAmount.add(
59
+ baseAssetAmountSwapped
60
+ );
61
+ userPositionAfter.quoteAssetAmount = userPositionAfter.quoteAssetAmount.add(
62
+ quoteAssetAmountSwapped
63
+ );
64
+
65
+ return [userAccountAfter, userPositionAfter, marketAfter];
66
+ } else {
67
+ const reversePosition = baseAssetAmountToTrade.gt(
68
+ userPosition.baseAssetAmount.abs()
69
+ );
70
+
71
+ if (reversePosition) {
72
+ const intermediateMarket = calculateNewMarketAfterTrade(
73
+ userPosition.baseAssetAmount,
74
+ findDirectionToClose(userPosition),
75
+ market
76
+ );
77
+
78
+ const { quoteAssetAmountSwapped: baseAssetValue } =
79
+ calculateAmountSwapped(market, intermediateMarket);
80
+
81
+ let pnl;
82
+ if (isVariant(currentPositionDirection, 'long')) {
83
+ pnl = baseAssetValue.sub(userPosition.quoteAssetAmount);
84
+ } else {
85
+ pnl = userPosition.quoteAssetAmount.sub(baseAssetValue);
86
+ }
87
+
88
+ userAccountAfter.collateral = userAccountAfter.collateral.add(pnl);
89
+
90
+ const baseAssetAmountLeft = baseAssetAmountToTrade.sub(
91
+ userPosition.baseAssetAmount.abs()
92
+ );
93
+
94
+ const marketAfter = calculateNewMarketAfterTrade(
95
+ baseAssetAmountLeft,
96
+ order.direction,
97
+ intermediateMarket
98
+ );
99
+
100
+ const { quoteAssetAmountSwapped, baseAssetAmountSwapped } =
101
+ calculateAmountSwapped(intermediateMarket, marketAfter);
102
+
103
+ userPositionAfter.quoteAssetAmount = quoteAssetAmountSwapped;
104
+ userPositionAfter.baseAssetAmount = baseAssetAmountSwapped;
105
+
106
+ return [userAccountAfter, userPositionAfter, marketAfter];
107
+ } else {
108
+ const marketAfter = calculateNewMarketAfterTrade(
109
+ baseAssetAmountToTrade,
110
+ order.direction,
111
+ market
112
+ );
113
+
114
+ const {
115
+ quoteAssetAmountSwapped: baseAssetValue,
116
+ baseAssetAmountSwapped,
117
+ } = calculateAmountSwapped(market, marketAfter);
118
+
119
+ const costBasisRealized = userPosition.quoteAssetAmount
120
+ .mul(baseAssetAmountSwapped.abs())
121
+ .div(userPosition.baseAssetAmount.abs());
122
+
123
+ let pnl;
124
+ if (isVariant(currentPositionDirection, 'long')) {
125
+ pnl = baseAssetValue.sub(costBasisRealized);
126
+ } else {
127
+ pnl = costBasisRealized.sub(baseAssetValue);
128
+ }
129
+
130
+ userAccountAfter.collateral = userAccountAfter.collateral.add(pnl);
131
+
132
+ userPositionAfter.baseAssetAmount = userPositionAfter.baseAssetAmount.add(
133
+ baseAssetAmountSwapped
134
+ );
135
+ userPositionAfter.quoteAssetAmount =
136
+ userPositionAfter.quoteAssetAmount.sub(costBasisRealized);
137
+
138
+ return [userAccountAfter, userPositionAfter, marketAfter];
139
+ }
140
+ }
141
+ }
142
+
143
+ function calculateAmountSwapped(
144
+ marketBefore: Market,
145
+ marketAfter: Market
146
+ ): { quoteAssetAmountSwapped: BN; baseAssetAmountSwapped: BN } {
147
+ return {
148
+ quoteAssetAmountSwapped: marketBefore.amm.quoteAssetReserve
149
+ .sub(marketAfter.amm.quoteAssetReserve)
150
+ .abs()
151
+ .mul(marketBefore.amm.pegMultiplier)
152
+ .div(PEG_PRECISION)
153
+ .div(AMM_TO_QUOTE_PRECISION_RATIO),
154
+ baseAssetAmountSwapped: marketBefore.amm.baseAssetReserve.sub(
155
+ marketAfter.amm.baseAssetReserve
156
+ ),
157
+ };
158
+ }
159
+
160
+ function calculateAmountToTrade(market: Market, order: Order): BN {
161
+ if (isVariant(order.orderType, 'limit')) {
162
+ return calculateAmountToTradeForLimit(market, order);
163
+ } else if (isVariant(order.orderType, 'stopLimit')) {
164
+ return calculateAmountToTradeForStopLimit(market, order);
165
+ } else if (isVariant(order.orderType, 'market')) {
166
+ // should never be a market order queued
167
+ return ZERO;
168
+ } else {
169
+ return calculateAmountToTradeForStop(market, order);
170
+ }
171
+ }
172
+
173
+ export function calculateAmountToTradeForLimit(
174
+ market: Market,
175
+ order: Order
176
+ ): BN {
177
+ const [maxAmountToTrade, direction] = calculateMaxBaseAssetAmountToTrade(
178
+ market.amm,
179
+ order.price
180
+ );
181
+
182
+ // Check that directions are the same
183
+ const sameDirection = isSameDirection(direction, order.direction);
184
+ if (!sameDirection) {
185
+ return ZERO;
186
+ }
187
+
188
+ return maxAmountToTrade.gt(order.baseAssetAmount)
189
+ ? order.baseAssetAmount
190
+ : maxAmountToTrade;
191
+ }
192
+
193
+ export function calculateAmountToTradeForStopLimit(
194
+ market: Market,
195
+ order: Order
196
+ ): BN {
197
+ if (order.baseAssetAmountFilled.eq(ZERO)) {
198
+ const baseAssetAmount = calculateAmountToTradeForStop(market, order);
199
+ if (baseAssetAmount.eq(ZERO)) {
200
+ return ZERO;
201
+ }
202
+ }
203
+
204
+ return calculateAmountToTradeForLimit(market, order);
205
+ }
206
+
207
+ function isSameDirection(
208
+ firstDirection: PositionDirection,
209
+ secondDirection: PositionDirection
210
+ ): boolean {
211
+ return (
212
+ (isVariant(firstDirection, 'long') && isVariant(secondDirection, 'long')) ||
213
+ (isVariant(firstDirection, 'short') && isVariant(secondDirection, 'short'))
214
+ );
215
+ }
216
+
217
+ function calculateAmountToTradeForStop(market: Market, order: Order): BN {
218
+ return isTriggerConditionSatisfied(market, order)
219
+ ? order.baseAssetAmount
220
+ : ZERO;
221
+ }
222
+
223
+ function isTriggerConditionSatisfied(market: Market, order: Order): boolean {
224
+ const markPrice = calculateMarkPrice(market);
225
+ if (isVariant(order.triggerCondition, 'above')) {
226
+ return markPrice.gt(order.triggerPrice);
227
+ } else {
228
+ return markPrice.lt(order.triggerPrice);
229
+ }
230
+ }