@drift-labs/sdk 0.1.18-master.8 → 0.1.18-orders.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 +32 -0
- package/lib/accounts/bulkAccountLoader.js +156 -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 +133 -0
- package/lib/accounts/types.d.ts +34 -3
- package/lib/accounts/utils.d.ts +1 -0
- package/lib/accounts/utils.js +7 -0
- package/lib/accounts/webSocketAccountSubscriber.d.ts +0 -1
- 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 +11 -4
- package/lib/admin.js +71 -17
- package/lib/assert/assert.d.ts +0 -1
- package/lib/clearingHouse.d.ts +35 -4
- package/lib/clearingHouse.js +294 -23
- package/lib/clearingHouseUser.d.ts +19 -18
- package/lib/clearingHouseUser.js +129 -101
- package/lib/config.d.ts +0 -1
- package/lib/config.js +1 -1
- package/lib/constants/markets.d.ts +0 -1
- package/lib/constants/numericConstants.d.ts +0 -1
- 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 +1077 -54
- 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 +0 -1
- 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 +0 -1
- 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 +30 -0
- package/lib/math/position.d.ts +4 -2
- package/lib/math/position.js +19 -5
- package/lib/math/trade.d.ts +0 -1
- package/lib/math/trade.js +16 -16
- package/lib/math/utils.d.ts +2 -3
- package/lib/math/utils.js +6 -3
- package/lib/mockUSDCFaucet.d.ts +0 -1
- package/lib/orderParams.d.ts +7 -0
- package/lib/orderParams.js +108 -0
- package/lib/orders.d.ts +5 -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 +144 -2
- 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 +3 -3
- package/src/accounts/bulkAccountLoader.ts +195 -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 +186 -0
- package/src/accounts/types.ts +43 -1
- package/src/accounts/utils.ts +3 -0
- 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 +111 -24
- package/src/clearingHouse.ts +399 -22
- package/src/clearingHouseUser.ts +179 -107
- package/src/config.ts +1 -1
- package/src/factory/clearingHouse.ts +125 -0
- package/src/factory/clearingHouseUser.ts +73 -0
- package/src/idl/clearing_house.json +1077 -54
- package/src/index.ts +11 -2
- package/src/math/amm.ts +47 -14
- package/src/math/market.ts +28 -2
- package/src/math/orders.ts +39 -0
- package/src/math/position.ts +23 -3
- package/src/math/utils.ts +1 -1
- package/src/orderParams.ts +151 -0
- package/src/orders.ts +230 -0
- package/src/token/index.ts +37 -0
- package/src/types.ts +128 -1
- 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/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,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
|
+
}
|
package/src/math/position.ts
CHANGED
|
@@ -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
|
@@ -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 getStopOrderParams(
|
|
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.STOP,
|
|
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 getStopLimitOrderParams(
|
|
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.STOP_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,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
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { AccountInfo, AccountLayout, u64 } from '@solana/spl-token';
|
|
2
|
+
import { PublicKey } from '@solana/web3.js';
|
|
3
|
+
|
|
4
|
+
export function parseTokenAccount(data: Buffer): AccountInfo {
|
|
5
|
+
const accountInfo = AccountLayout.decode(data);
|
|
6
|
+
accountInfo.mint = new PublicKey(accountInfo.mint);
|
|
7
|
+
accountInfo.owner = new PublicKey(accountInfo.owner);
|
|
8
|
+
accountInfo.amount = u64.fromBuffer(accountInfo.amount);
|
|
9
|
+
|
|
10
|
+
if (accountInfo.delegateOption === 0) {
|
|
11
|
+
accountInfo.delegate = null;
|
|
12
|
+
// eslint-disable-next-line new-cap
|
|
13
|
+
accountInfo.delegatedAmount = new u64(0);
|
|
14
|
+
} else {
|
|
15
|
+
accountInfo.delegate = new PublicKey(accountInfo.delegate);
|
|
16
|
+
accountInfo.delegatedAmount = u64.fromBuffer(accountInfo.delegatedAmount);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
accountInfo.isInitialized = accountInfo.state !== 0;
|
|
20
|
+
accountInfo.isFrozen = accountInfo.state === 2;
|
|
21
|
+
|
|
22
|
+
if (accountInfo.isNativeOption === 1) {
|
|
23
|
+
accountInfo.rentExemptReserve = u64.fromBuffer(accountInfo.isNative);
|
|
24
|
+
accountInfo.isNative = true;
|
|
25
|
+
} else {
|
|
26
|
+
accountInfo.rentExemptReserve = null;
|
|
27
|
+
accountInfo.isNative = false;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (accountInfo.closeAuthorityOption === 0) {
|
|
31
|
+
accountInfo.closeAuthority = null;
|
|
32
|
+
} else {
|
|
33
|
+
accountInfo.closeAuthority = new PublicKey(accountInfo.closeAuthority);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return accountInfo;
|
|
37
|
+
}
|