@drift-labs/sdk 0.1.19-master.1 → 0.1.21-master.3
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.
- package/lib/accounts/bulkAccountLoader.d.ts +31 -0
- package/lib/accounts/bulkAccountLoader.js +177 -0
- package/lib/accounts/bulkUserSubscription.d.ts +7 -0
- package/lib/accounts/bulkUserSubscription.js +28 -0
- package/lib/accounts/pollingClearingHouseAccountSubscriber.d.ts +49 -0
- package/lib/accounts/pollingClearingHouseAccountSubscriber.js +228 -0
- package/lib/accounts/pollingTokenAccountSubscriber.d.ts +25 -0
- package/lib/accounts/pollingTokenAccountSubscriber.js +79 -0
- package/lib/accounts/pollingUserAccountSubscriber.d.ts +32 -0
- package/lib/accounts/pollingUserAccountSubscriber.js +136 -0
- package/lib/accounts/types.d.ts +38 -3
- package/lib/accounts/utils.d.ts +1 -0
- package/lib/accounts/utils.js +7 -0
- package/lib/accounts/webSocketAccountSubscriber.d.ts +6 -3
- package/lib/accounts/webSocketAccountSubscriber.js +43 -12
- package/lib/accounts/{defaultClearingHouseAccountSubscriber.d.ts → webSocketClearingHouseAccountSubscriber.d.ts} +8 -3
- package/lib/accounts/{defaultClearingHouseAccountSubscriber.js → webSocketClearingHouseAccountSubscriber.js} +30 -4
- package/lib/accounts/{defaultUserAccountSubscriber.d.ts → webSocketUserAccountSubscriber.d.ts} +6 -3
- package/lib/accounts/{defaultUserAccountSubscriber.js → webSocketUserAccountSubscriber.js} +16 -4
- package/lib/addresses.d.ts +4 -1
- package/lib/addresses.js +28 -1
- package/lib/admin.d.ts +10 -4
- package/lib/admin.js +54 -17
- package/lib/assert/assert.d.ts +0 -1
- package/lib/clearingHouse.d.ts +39 -4
- package/lib/clearingHouse.js +334 -23
- package/lib/clearingHouseUser.d.ts +26 -20
- package/lib/clearingHouseUser.js +149 -118
- package/lib/config.d.ts +0 -1
- package/lib/constants/markets.d.ts +2 -2
- package/lib/constants/markets.js +28 -15
- package/lib/constants/numericConstants.d.ts +4 -2
- package/lib/constants/numericConstants.js +16 -17
- package/lib/examples/makeTradeExample.d.ts +0 -1
- package/lib/examples/makeTradeExample.js +6 -6
- package/lib/factory/clearingHouse.d.ts +25 -0
- package/lib/factory/clearingHouse.js +64 -0
- package/lib/factory/clearingHouseUser.d.ts +19 -0
- package/lib/factory/clearingHouseUser.js +34 -0
- package/lib/idl/clearing_house.json +1066 -39
- package/lib/index.d.ts +11 -3
- package/lib/index.js +12 -2
- package/lib/math/amm.d.ts +1 -1
- package/lib/math/amm.js +38 -15
- package/lib/math/conversion.d.ts +1 -2
- package/lib/math/conversion.js +1 -1
- package/lib/math/funding.d.ts +0 -1
- package/lib/math/funding.js +1 -1
- package/lib/math/insuranceFund.d.ts +2 -2
- package/lib/math/insuranceFund.js +3 -6
- package/lib/math/market.d.ts +2 -2
- package/lib/math/market.js +12 -2
- package/lib/math/orders.d.ts +3 -0
- package/lib/math/orders.js +32 -0
- package/lib/math/position.d.ts +6 -3
- package/lib/math/position.js +21 -10
- package/lib/math/trade.d.ts +0 -1
- package/lib/math/trade.js +16 -16
- package/lib/math/utils.d.ts +2 -2
- package/lib/math/utils.js +3 -3
- package/lib/mockUSDCFaucet.d.ts +2 -2
- package/lib/orderParams.d.ts +7 -0
- package/lib/orderParams.js +108 -0
- package/lib/orders.d.ts +6 -0
- package/lib/orders.js +136 -0
- package/lib/pythClient.d.ts +0 -1
- package/lib/pythClient.js +1 -1
- package/lib/token/index.d.ts +3 -0
- package/lib/token/index.js +38 -0
- package/lib/tx/defaultTxSender.d.ts +0 -1
- package/lib/tx/types.d.ts +0 -1
- package/lib/tx/utils.d.ts +0 -1
- package/lib/types.d.ts +147 -3
- package/lib/types.js +36 -1
- package/lib/util/computeUnits.d.ts +0 -1
- package/lib/util/tps.d.ts +0 -1
- package/lib/wallet.d.ts +0 -1
- package/package.json +11 -3
- package/src/accounts/bulkAccountLoader.ts +215 -0
- package/src/accounts/bulkUserSubscription.ts +28 -0
- package/src/accounts/pollingClearingHouseAccountSubscriber.ts +326 -0
- package/src/accounts/pollingTokenAccountSubscriber.ts +99 -0
- package/src/accounts/pollingUserAccountSubscriber.ts +194 -0
- package/src/accounts/types.ts +48 -1
- package/src/accounts/utils.ts +3 -0
- package/src/accounts/webSocketAccountSubscriber.ts +67 -17
- package/src/accounts/{defaultClearingHouseAccountSubscriber.ts → webSocketClearingHouseAccountSubscriber.ts} +51 -1
- package/src/accounts/{defaultUserAccountSubscriber.ts → webSocketUserAccountSubscriber.ts} +33 -3
- package/src/addresses.ts +37 -0
- package/src/admin.ts +92 -24
- package/src/clearingHouse.ts +455 -22
- package/src/clearingHouseUser.ts +190 -108
- package/src/constants/markets.ts +17 -1
- package/src/constants/numericConstants.ts +3 -1
- package/src/examples/makeTradeExample.ts +4 -1
- package/src/factory/clearingHouse.ts +125 -0
- package/src/factory/clearingHouseUser.ts +73 -0
- package/src/idl/clearing_house.json +1066 -39
- package/src/index.ts +11 -2
- package/src/math/amm.ts +47 -14
- package/src/math/conversion.ts +1 -1
- package/src/math/insuranceFund.ts +1 -1
- package/src/math/market.ts +28 -2
- package/src/math/orders.ts +44 -0
- package/src/math/position.ts +24 -4
- package/src/math/utils.ts +1 -1
- package/src/mockUSDCFaucet.ts +1 -1
- package/src/orderParams.ts +151 -0
- package/src/orders.ts +236 -0
- package/src/token/index.ts +37 -0
- package/src/types.ts +130 -2
- package/tsconfig.json +0 -1
- package/lib/accounts/defaultClearingHouseAccountSubscriber.d.ts.map +0 -1
- package/lib/accounts/defaultUserAccountSubscriber.d.ts.map +0 -1
- package/lib/accounts/types.d.ts.map +0 -1
- package/lib/accounts/webSocketAccountSubscriber.d.ts.map +0 -1
- package/lib/addresses.d.ts.map +0 -1
- package/lib/admin.d.ts.map +0 -1
- package/lib/assert/assert.d.ts.map +0 -1
- package/lib/clearingHouse.d.ts.map +0 -1
- package/lib/clearingHouseUser.d.ts.map +0 -1
- package/lib/config.d.ts.map +0 -1
- package/lib/constants/markets.d.ts.map +0 -1
- package/lib/constants/numericConstants.d.ts.map +0 -1
- package/lib/examples/makeTradeExample.d.ts.map +0 -1
- package/lib/index.d.ts.map +0 -1
- package/lib/math/amm.d.ts.map +0 -1
- package/lib/math/conversion.d.ts.map +0 -1
- package/lib/math/funding.d.ts.map +0 -1
- package/lib/math/insuranceFund.d.ts.map +0 -1
- package/lib/math/market.d.ts.map +0 -1
- package/lib/math/position.d.ts.map +0 -1
- package/lib/math/trade.d.ts.map +0 -1
- package/lib/math/utils.d.ts.map +0 -1
- package/lib/mockUSDCFaucet.d.ts.map +0 -1
- package/lib/pythClient.d.ts.map +0 -1
- package/lib/tx/defaultTxSender.d.ts.map +0 -1
- package/lib/tx/types.d.ts.map +0 -1
- package/lib/tx/utils.d.ts.map +0 -1
- package/lib/types.d.ts.map +0 -1
- package/lib/util/computeUnits.d.ts.map +0 -1
- package/lib/util/tps.d.ts.map +0 -1
- package/lib/wallet.d.ts.map +0 -1
package/src/index.ts
CHANGED
|
@@ -1,15 +1,21 @@
|
|
|
1
1
|
import { BN } from '@project-serum/anchor';
|
|
2
|
+
import { PublicKey } from '@solana/web3.js';
|
|
2
3
|
|
|
3
4
|
export * from './mockUSDCFaucet';
|
|
4
5
|
export * from './pythClient';
|
|
5
6
|
export * from './types';
|
|
6
7
|
export * from './constants/markets';
|
|
7
|
-
export * from './accounts/
|
|
8
|
+
export * from './accounts/webSocketClearingHouseAccountSubscriber';
|
|
9
|
+
export * from './accounts/bulkAccountLoader';
|
|
10
|
+
export * from './accounts/pollingClearingHouseAccountSubscriber';
|
|
11
|
+
export * from './accounts/pollingTokenAccountSubscriber';
|
|
8
12
|
export * from './accounts/types';
|
|
9
13
|
export * from './addresses';
|
|
10
14
|
export * from './admin';
|
|
11
15
|
export * from './clearingHouseUser';
|
|
12
16
|
export * from './clearingHouse';
|
|
17
|
+
export * from './factory/clearingHouse';
|
|
18
|
+
export * from './factory/clearingHouseUser';
|
|
13
19
|
export * from './math/conversion';
|
|
14
20
|
export * from './math/funding';
|
|
15
21
|
export * from './math/insuranceFund';
|
|
@@ -17,6 +23,9 @@ export * from './math/market';
|
|
|
17
23
|
export * from './math/position';
|
|
18
24
|
export * from './math/amm';
|
|
19
25
|
export * from './math/trade';
|
|
26
|
+
export * from './math/orders';
|
|
27
|
+
export * from './orders';
|
|
28
|
+
export * from './orderParams';
|
|
20
29
|
export * from './wallet';
|
|
21
30
|
export * from './types';
|
|
22
31
|
export * from './math/utils';
|
|
@@ -25,4 +34,4 @@ export * from './constants/numericConstants';
|
|
|
25
34
|
export * from './util/computeUnits';
|
|
26
35
|
export * from './util/tps';
|
|
27
36
|
|
|
28
|
-
export { BN };
|
|
37
|
+
export { BN, PublicKey };
|
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 {
|
|
9
|
+
import {
|
|
10
|
+
AMM,
|
|
11
|
+
PositionDirection,
|
|
12
|
+
SwapDirection,
|
|
13
|
+
Market,
|
|
14
|
+
isVariant,
|
|
15
|
+
} from '../types';
|
|
10
16
|
import { assert } from '../assert/assert';
|
|
11
|
-
import {
|
|
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
|
+
}
|
package/src/math/conversion.ts
CHANGED
package/src/math/market.ts
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { BN } from '@project-serum/anchor';
|
|
2
|
-
import { Market } from '../types';
|
|
3
|
-
import {
|
|
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,44 @@
|
|
|
1
|
+
import { ClearingHouseUser } from '../clearingHouseUser';
|
|
2
|
+
import { isVariant, Order } from '../types';
|
|
3
|
+
import { ZERO, TWO } 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
|
+
const baseAssetAmountToFill = order.baseAssetAmount.sub(
|
|
36
|
+
order.baseAssetAmountFilled
|
|
37
|
+
);
|
|
38
|
+
// if order will flip position
|
|
39
|
+
if (baseAssetAmountToFill.gt(position.baseAssetAmount.abs().mul(TWO))) {
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return false;
|
|
44
|
+
}
|
package/src/math/position.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import BN from '
|
|
1
|
+
import { BN } from '../';
|
|
2
2
|
import {
|
|
3
3
|
AMM_RESERVE_PRECISION,
|
|
4
4
|
AMM_TIMES_PEG_TO_QUOTE_PRECISION_RATIO,
|
|
@@ -27,9 +27,7 @@ export function calculateBaseAssetValue(
|
|
|
27
27
|
return ZERO;
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
const directionToClose = userPosition
|
|
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
|
+
}
|
package/src/math/utils.ts
CHANGED
package/src/mockUSDCFaucet.ts
CHANGED
|
@@ -0,0 +1,151 @@
|
|
|
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
|
+
userOrderId = 0
|
|
19
|
+
): OrderParams {
|
|
20
|
+
return {
|
|
21
|
+
orderType: OrderType.LIMIT,
|
|
22
|
+
userOrderId,
|
|
23
|
+
marketIndex,
|
|
24
|
+
direction,
|
|
25
|
+
quoteAssetAmount: ZERO,
|
|
26
|
+
baseAssetAmount,
|
|
27
|
+
price,
|
|
28
|
+
reduceOnly,
|
|
29
|
+
postOnly: false,
|
|
30
|
+
immediateOrCancel: false,
|
|
31
|
+
positionLimit: ZERO,
|
|
32
|
+
padding0: true,
|
|
33
|
+
padding1: ZERO,
|
|
34
|
+
optionalAccounts: {
|
|
35
|
+
discountToken,
|
|
36
|
+
referrer,
|
|
37
|
+
},
|
|
38
|
+
triggerCondition: OrderTriggerCondition.ABOVE,
|
|
39
|
+
triggerPrice: ZERO,
|
|
40
|
+
oraclePriceOffset: ZERO,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function getTriggerMarketOrderParams(
|
|
45
|
+
marketIndex: BN,
|
|
46
|
+
direction: PositionDirection,
|
|
47
|
+
baseAssetAmount: BN,
|
|
48
|
+
triggerPrice: BN,
|
|
49
|
+
triggerCondition: OrderTriggerCondition,
|
|
50
|
+
reduceOnly: boolean,
|
|
51
|
+
discountToken = false,
|
|
52
|
+
referrer = false,
|
|
53
|
+
userOrderId = 0
|
|
54
|
+
): OrderParams {
|
|
55
|
+
return {
|
|
56
|
+
orderType: OrderType.TRIGGER_MARKET,
|
|
57
|
+
userOrderId,
|
|
58
|
+
marketIndex,
|
|
59
|
+
direction,
|
|
60
|
+
quoteAssetAmount: ZERO,
|
|
61
|
+
baseAssetAmount,
|
|
62
|
+
price: ZERO,
|
|
63
|
+
reduceOnly,
|
|
64
|
+
postOnly: false,
|
|
65
|
+
immediateOrCancel: false,
|
|
66
|
+
positionLimit: ZERO,
|
|
67
|
+
padding0: true,
|
|
68
|
+
padding1: ZERO,
|
|
69
|
+
optionalAccounts: {
|
|
70
|
+
discountToken,
|
|
71
|
+
referrer,
|
|
72
|
+
},
|
|
73
|
+
triggerCondition,
|
|
74
|
+
triggerPrice,
|
|
75
|
+
oraclePriceOffset: ZERO,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export function getTriggerLimitOrderParams(
|
|
80
|
+
marketIndex: BN,
|
|
81
|
+
direction: PositionDirection,
|
|
82
|
+
baseAssetAmount: BN,
|
|
83
|
+
price: BN,
|
|
84
|
+
triggerPrice: BN,
|
|
85
|
+
triggerCondition: OrderTriggerCondition,
|
|
86
|
+
reduceOnly: boolean,
|
|
87
|
+
discountToken = false,
|
|
88
|
+
referrer = false,
|
|
89
|
+
userOrderId = 0
|
|
90
|
+
): OrderParams {
|
|
91
|
+
return {
|
|
92
|
+
orderType: OrderType.TRIGGER_LIMIT,
|
|
93
|
+
userOrderId,
|
|
94
|
+
marketIndex,
|
|
95
|
+
direction,
|
|
96
|
+
quoteAssetAmount: ZERO,
|
|
97
|
+
baseAssetAmount,
|
|
98
|
+
price,
|
|
99
|
+
reduceOnly,
|
|
100
|
+
postOnly: false,
|
|
101
|
+
immediateOrCancel: false,
|
|
102
|
+
positionLimit: ZERO,
|
|
103
|
+
padding0: true,
|
|
104
|
+
padding1: ZERO,
|
|
105
|
+
optionalAccounts: {
|
|
106
|
+
discountToken,
|
|
107
|
+
referrer,
|
|
108
|
+
},
|
|
109
|
+
triggerCondition,
|
|
110
|
+
triggerPrice,
|
|
111
|
+
oraclePriceOffset: ZERO,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export function getMarketOrderParams(
|
|
116
|
+
marketIndex: BN,
|
|
117
|
+
direction: PositionDirection,
|
|
118
|
+
quoteAssetAmount: BN,
|
|
119
|
+
baseAssetAmount: BN,
|
|
120
|
+
reduceOnly: boolean,
|
|
121
|
+
price = ZERO,
|
|
122
|
+
discountToken = false,
|
|
123
|
+
referrer = false
|
|
124
|
+
): OrderParams {
|
|
125
|
+
if (baseAssetAmount.eq(ZERO) && quoteAssetAmount.eq(ZERO)) {
|
|
126
|
+
throw Error('baseAssetAmount or quoteAssetAmount must be zero');
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return {
|
|
130
|
+
orderType: OrderType.MARKET,
|
|
131
|
+
userOrderId: 0,
|
|
132
|
+
marketIndex,
|
|
133
|
+
direction,
|
|
134
|
+
quoteAssetAmount,
|
|
135
|
+
baseAssetAmount,
|
|
136
|
+
price,
|
|
137
|
+
reduceOnly,
|
|
138
|
+
postOnly: false,
|
|
139
|
+
immediateOrCancel: false,
|
|
140
|
+
positionLimit: ZERO,
|
|
141
|
+
padding0: true,
|
|
142
|
+
padding1: ZERO,
|
|
143
|
+
optionalAccounts: {
|
|
144
|
+
discountToken,
|
|
145
|
+
referrer,
|
|
146
|
+
},
|
|
147
|
+
triggerCondition: OrderTriggerCondition.ABOVE,
|
|
148
|
+
triggerPrice: ZERO,
|
|
149
|
+
oraclePriceOffset: ZERO,
|
|
150
|
+
};
|
|
151
|
+
}
|
package/src/orders.ts
ADDED
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
import {
|
|
2
|
+
isVariant,
|
|
3
|
+
Market,
|
|
4
|
+
Order,
|
|
5
|
+
PositionDirection,
|
|
6
|
+
UserAccount,
|
|
7
|
+
UserPosition,
|
|
8
|
+
} from './types';
|
|
9
|
+
import { BN } from '.';
|
|
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, 'triggerLimit')) {
|
|
164
|
+
return calculateAmountToTradeForTriggerLimit(market, order);
|
|
165
|
+
} else if (isVariant(order.orderType, 'market')) {
|
|
166
|
+
// should never be a market order queued
|
|
167
|
+
return ZERO;
|
|
168
|
+
} else {
|
|
169
|
+
return calculateAmountToTradeForTriggerMarket(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 calculateAmountToTradeForTriggerLimit(
|
|
194
|
+
market: Market,
|
|
195
|
+
order: Order
|
|
196
|
+
): BN {
|
|
197
|
+
if (order.baseAssetAmountFilled.eq(ZERO)) {
|
|
198
|
+
const baseAssetAmount = calculateAmountToTradeForTriggerMarket(
|
|
199
|
+
market,
|
|
200
|
+
order
|
|
201
|
+
);
|
|
202
|
+
if (baseAssetAmount.eq(ZERO)) {
|
|
203
|
+
return ZERO;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
return calculateAmountToTradeForLimit(market, order);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
function isSameDirection(
|
|
211
|
+
firstDirection: PositionDirection,
|
|
212
|
+
secondDirection: PositionDirection
|
|
213
|
+
): boolean {
|
|
214
|
+
return (
|
|
215
|
+
(isVariant(firstDirection, 'long') && isVariant(secondDirection, 'long')) ||
|
|
216
|
+
(isVariant(firstDirection, 'short') && isVariant(secondDirection, 'short'))
|
|
217
|
+
);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
function calculateAmountToTradeForTriggerMarket(
|
|
221
|
+
market: Market,
|
|
222
|
+
order: Order
|
|
223
|
+
): BN {
|
|
224
|
+
return isTriggerConditionSatisfied(market, order)
|
|
225
|
+
? order.baseAssetAmount
|
|
226
|
+
: ZERO;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
function isTriggerConditionSatisfied(market: Market, order: Order): boolean {
|
|
230
|
+
const markPrice = calculateMarkPrice(market);
|
|
231
|
+
if (isVariant(order.triggerCondition, 'above')) {
|
|
232
|
+
return markPrice.gt(order.triggerPrice);
|
|
233
|
+
} else {
|
|
234
|
+
return markPrice.lt(order.triggerPrice);
|
|
235
|
+
}
|
|
236
|
+
}
|