@drift-labs/sdk 0.2.0-temp.1 → 2.0.0
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/README.md +27 -29
- package/lib/accounts/bulkAccountLoader.d.ts +2 -0
- package/lib/accounts/bulkAccountLoader.js +36 -29
- package/lib/accounts/bulkUserStatsSubscription.d.ts +7 -0
- package/lib/accounts/bulkUserStatsSubscription.js +21 -0
- package/lib/accounts/bulkUserSubscription.d.ts +2 -2
- package/lib/accounts/bulkUserSubscription.js +0 -1
- package/lib/accounts/fetch.d.ts +2 -1
- package/lib/accounts/fetch.js +9 -1
- package/lib/accounts/{pollingClearingHouseAccountSubscriber.d.ts → pollingDriftClientAccountSubscriber.d.ts} +20 -25
- package/lib/accounts/{pollingClearingHouseAccountSubscriber.js → pollingDriftClientAccountSubscriber.js} +45 -49
- package/lib/accounts/{pollingOracleSubscriber.d.ts → pollingOracleAccountSubscriber.d.ts} +2 -2
- package/lib/accounts/{pollingOracleSubscriber.js → pollingOracleAccountSubscriber.js} +3 -3
- package/lib/accounts/pollingUserAccountSubscriber.d.ts +1 -1
- package/lib/accounts/pollingUserAccountSubscriber.js +5 -11
- package/lib/accounts/pollingUserStatsAccountSubscriber.d.ts +27 -0
- package/lib/accounts/pollingUserStatsAccountSubscriber.js +107 -0
- package/lib/accounts/types.d.ts +26 -15
- package/lib/accounts/webSocketDriftClientAccountSubscriber.d.ts +49 -0
- package/lib/accounts/{webSocketClearingHouseAccountSubscriber.js → webSocketDriftClientAccountSubscriber.js} +47 -45
- package/lib/accounts/webSocketUserStatsAccountSubsriber.d.ts +20 -0
- package/lib/accounts/webSocketUserStatsAccountSubsriber.js +47 -0
- package/lib/addresses/marketAddresses.d.ts +1 -3
- package/lib/addresses/marketAddresses.js +1 -1
- package/lib/addresses/pda.d.ts +15 -9
- package/lib/addresses/pda.js +73 -35
- package/lib/adminClient.d.ts +65 -0
- package/lib/adminClient.js +637 -0
- package/lib/config.d.ts +10 -10
- package/lib/config.js +26 -22
- package/lib/constants/numericConstants.d.ts +29 -12
- package/lib/constants/numericConstants.js +40 -21
- package/lib/constants/perpMarkets.d.ts +18 -0
- package/lib/constants/{markets.js → perpMarkets.js} +20 -9
- package/lib/constants/spotMarkets.d.ts +19 -0
- package/lib/constants/spotMarkets.js +63 -0
- package/lib/dlob/DLOB.d.ts +82 -0
- package/lib/dlob/DLOB.js +694 -0
- package/lib/dlob/DLOBNode.d.ts +54 -0
- package/lib/dlob/DLOBNode.js +77 -0
- package/lib/dlob/NodeList.d.ts +27 -0
- package/lib/dlob/NodeList.js +144 -0
- package/lib/driftClient.d.ts +234 -0
- package/lib/driftClient.js +2108 -0
- package/lib/{clearingHouseConfig.d.ts → driftClientConfig.d.ts} +9 -9
- package/lib/{clearingHouseConfig.js → driftClientConfig.js} +0 -0
- package/lib/events/eventList.js +3 -0
- package/lib/events/eventSubscriber.d.ts +5 -2
- package/lib/events/eventSubscriber.js +25 -11
- package/lib/events/fetchLogs.d.ts +13 -2
- package/lib/events/fetchLogs.js +45 -14
- package/lib/events/pollingLogProvider.d.ts +2 -1
- package/lib/events/pollingLogProvider.js +7 -3
- package/lib/events/sort.js +8 -11
- package/lib/events/types.d.ts +11 -3
- package/lib/events/types.js +8 -0
- package/lib/events/webSocketLogProvider.js +1 -1
- package/lib/examples/makeTradeExample.js +30 -18
- package/lib/factory/bigNum.d.ts +8 -4
- package/lib/factory/bigNum.js +109 -19
- package/lib/idl/drift.json +8392 -0
- package/lib/index.d.ts +29 -12
- package/lib/index.js +29 -12
- package/lib/math/amm.d.ts +9 -6
- package/lib/math/amm.js +91 -38
- package/lib/math/conversion.js +1 -1
- package/lib/math/exchangeStatus.d.ts +4 -0
- package/lib/math/exchangeStatus.js +18 -0
- package/lib/math/funding.d.ts +6 -6
- package/lib/math/funding.js +23 -21
- package/lib/math/insurance.d.ts +4 -0
- package/lib/math/insurance.js +27 -0
- package/lib/math/margin.d.ts +11 -0
- package/lib/math/margin.js +82 -0
- package/lib/math/market.d.ts +14 -9
- package/lib/math/market.js +70 -10
- package/lib/math/oracles.d.ts +4 -0
- package/lib/math/oracles.js +36 -8
- package/lib/math/orders.d.ts +16 -6
- package/lib/math/orders.js +97 -17
- package/lib/math/position.d.ts +27 -13
- package/lib/math/position.js +91 -37
- package/lib/math/repeg.js +17 -8
- package/lib/math/spotBalance.d.ts +22 -0
- package/lib/math/spotBalance.js +192 -0
- package/lib/math/spotMarket.d.ts +4 -0
- package/lib/math/spotMarket.js +8 -0
- package/lib/math/spotPosition.d.ts +6 -0
- package/lib/math/spotPosition.js +23 -0
- package/lib/math/trade.d.ts +10 -10
- package/lib/math/trade.js +27 -31
- package/lib/oracles/pythClient.js +1 -1
- package/lib/oracles/quoteAssetOracleClient.js +1 -1
- package/lib/oracles/switchboardClient.js +1 -1
- package/lib/orderParams.d.ts +4 -4
- package/lib/orderParams.js +12 -4
- package/lib/serum/serumFulfillmentConfigMap.d.ts +10 -0
- package/lib/serum/serumFulfillmentConfigMap.js +17 -0
- package/lib/serum/serumSubscriber.d.ts +27 -0
- package/lib/serum/serumSubscriber.js +56 -0
- package/lib/serum/types.d.ts +11 -0
- package/{src/oracles → lib/serum}/types.js +0 -0
- package/lib/tokenFaucet.d.ts +1 -0
- package/lib/tokenFaucet.js +23 -12
- package/lib/tx/retryTxSender.d.ts +1 -1
- package/lib/tx/retryTxSender.js +13 -4
- package/lib/tx/types.d.ts +1 -1
- package/lib/types.d.ts +631 -222
- package/lib/types.js +137 -24
- package/lib/user.d.ts +228 -0
- package/lib/user.js +959 -0
- package/lib/userConfig.d.ts +14 -0
- package/lib/{clearingHouseUserConfig.js → userConfig.js} +0 -0
- package/lib/userMap/userMap.d.ts +41 -0
- package/lib/userMap/userMap.js +85 -0
- package/lib/userMap/userStatsMap.d.ts +19 -0
- package/lib/userMap/userStatsMap.js +68 -0
- package/lib/userName.d.ts +1 -0
- package/lib/userName.js +3 -2
- package/lib/userStats.d.ts +18 -0
- package/lib/userStats.js +49 -0
- package/lib/userStatsConfig.d.ts +14 -0
- package/{src/clearingHouseConfig.js → lib/userStatsConfig.js} +0 -0
- package/lib/util/getTokenAddress.d.ts +2 -0
- package/lib/util/getTokenAddress.js +9 -0
- package/package.json +12 -5
- package/src/accounts/bulkAccountLoader.ts +44 -34
- package/src/accounts/bulkUserStatsSubscription.ts +33 -0
- package/src/accounts/bulkUserSubscription.ts +2 -3
- package/src/accounts/fetch.ts +27 -2
- package/src/accounts/{pollingClearingHouseAccountSubscriber.ts → pollingDriftClientAccountSubscriber.ts} +65 -75
- package/src/accounts/{pollingOracleSubscriber.ts → pollingOracleAccountSubscriber.ts} +2 -2
- package/src/accounts/pollingUserAccountSubscriber.ts +5 -12
- package/src/accounts/pollingUserStatsAccountSubscriber.ts +166 -0
- package/src/accounts/types.ts +35 -15
- package/src/accounts/{webSocketClearingHouseAccountSubscriber.ts → webSocketDriftClientAccountSubscriber.ts} +78 -73
- package/src/accounts/webSocketUserStatsAccountSubsriber.ts +80 -0
- package/src/addresses/marketAddresses.ts +3 -4
- package/src/addresses/pda.ts +105 -33
- package/src/adminClient.ts +1207 -0
- package/src/config.ts +41 -34
- package/src/constants/numericConstants.ts +59 -26
- package/src/constants/{markets.ts → perpMarkets.ts} +22 -11
- package/src/constants/spotMarkets.ts +83 -0
- package/src/dlob/DLOB.ts +1120 -0
- package/src/dlob/DLOBNode.ts +155 -0
- package/src/dlob/NodeList.ts +195 -0
- package/src/driftClient.ts +3594 -0
- package/src/{clearingHouseConfig.ts → driftClientConfig.ts} +9 -8
- package/src/events/eventList.ts +3 -0
- package/src/events/eventSubscriber.ts +36 -14
- package/src/events/fetchLogs.ts +60 -15
- package/src/events/pollingLogProvider.ts +11 -3
- package/src/events/sort.ts +11 -15
- package/src/events/types.ts +27 -2
- package/src/events/webSocketLogProvider.ts +1 -1
- package/src/examples/makeTradeExample.js +152 -75
- package/src/examples/makeTradeExample.ts +44 -28
- package/src/factory/bigNum.ts +150 -22
- package/src/idl/drift.json +8392 -0
- package/src/idl/pyth.json +98 -2
- package/src/index.ts +29 -12
- package/src/math/amm.ts +161 -48
- package/src/math/conversion.ts +2 -2
- package/src/math/exchangeStatus.ts +31 -0
- package/src/math/funding.ts +41 -31
- package/src/math/insurance.ts +35 -0
- package/src/math/margin.ts +133 -0
- package/src/math/market.ts +143 -14
- package/src/math/oracles.ts +63 -9
- package/src/math/orders.ts +168 -26
- package/src/math/position.ts +133 -59
- package/src/math/repeg.ts +19 -9
- package/src/math/spotBalance.ts +319 -0
- package/src/math/spotMarket.ts +9 -0
- package/src/math/spotPosition.ts +47 -0
- package/src/math/trade.ts +33 -37
- package/src/oracles/pythClient.ts +2 -2
- package/src/oracles/quoteAssetOracleClient.ts +2 -2
- package/src/oracles/switchboardClient.ts +2 -2
- package/src/orderParams.ts +16 -8
- package/src/serum/serumFulfillmentConfigMap.ts +26 -0
- package/src/serum/serumSubscriber.ts +99 -0
- package/src/serum/types.ts +13 -0
- package/src/tokenFaucet.ts +38 -15
- package/src/tx/retryTxSender.ts +16 -5
- package/src/tx/types.ts +2 -1
- package/src/types.ts +594 -195
- package/src/user.ts +1599 -0
- package/src/{clearingHouseUserConfig.ts → userConfig.ts} +5 -5
- package/src/userMap/userMap.ts +124 -0
- package/src/userMap/userStatsMap.ts +108 -0
- package/src/userName.ts +2 -1
- package/src/userStats.ts +75 -0
- package/src/userStatsConfig.ts +18 -0
- package/src/util/getTokenAddress.ts +18 -0
- package/tests/bn/test.ts +46 -11
- package/tests/dlob/helpers.ts +619 -0
- package/tests/dlob/test.ts +4586 -0
- package/lib/accounts/webSocketClearingHouseAccountSubscriber.d.ts +0 -49
- package/lib/admin.d.ts +0 -44
- package/lib/admin.js +0 -438
- package/lib/clearingHouse.d.ts +0 -146
- package/lib/clearingHouse.js +0 -1154
- package/lib/clearingHouseUser.d.ts +0 -187
- package/lib/clearingHouseUser.js +0 -634
- package/lib/clearingHouseUserConfig.d.ts +0 -14
- package/lib/constants/banks.d.ts +0 -16
- package/lib/constants/banks.js +0 -41
- package/lib/constants/markets.d.ts +0 -19
- package/lib/idl/clearing_house.json +0 -4464
- package/lib/math/bankBalance.d.ts +0 -9
- package/lib/math/bankBalance.js +0 -75
- package/lib/math/state.d.ts +0 -8
- package/lib/math/state.js +0 -15
- package/lib/orders.d.ts +0 -8
- package/lib/orders.js +0 -134
- package/src/accounts/bulkAccountLoader.js +0 -197
- package/src/accounts/bulkUserSubscription.js +0 -33
- package/src/accounts/pollingClearingHouseAccountSubscriber.js +0 -311
- package/src/accounts/pollingOracleSubscriber.js +0 -93
- package/src/accounts/pollingTokenAccountSubscriber.js +0 -90
- package/src/accounts/pollingUserAccountSubscriber.js +0 -132
- package/src/accounts/types.js +0 -10
- package/src/accounts/utils.js +0 -7
- package/src/accounts/webSocketAccountSubscriber.js +0 -93
- package/src/accounts/webSocketClearingHouseAccountSubscriber.js +0 -233
- package/src/accounts/webSocketUserAccountSubscriber.js +0 -62
- package/src/addresses/marketAddresses.js +0 -26
- package/src/admin.js +0 -517
- package/src/admin.ts +0 -730
- package/src/clearingHouse.ts +0 -1828
- package/src/clearingHouseUser.ts +0 -978
- package/src/clearingHouseUserConfig.js +0 -2
- package/src/config.js +0 -67
- package/src/constants/banks.js +0 -42
- package/src/constants/banks.ts +0 -50
- package/src/constants/markets.js +0 -42
- package/src/constants/numericConstants.js +0 -41
- package/src/events/eventSubscriber.js +0 -139
- package/src/events/fetchLogs.js +0 -50
- package/src/events/pollingLogProvider.js +0 -64
- package/src/events/sort.js +0 -44
- package/src/events/txEventCache.js +0 -71
- package/src/events/types.js +0 -20
- package/src/events/webSocketLogProvider.js +0 -41
- package/src/factory/bigNum.js +0 -390
- package/src/factory/oracleClient.js +0 -20
- package/src/idl/clearing_house.json +0 -4464
- package/src/index.js +0 -69
- package/src/math/amm.js +0 -369
- package/src/math/auction.js +0 -42
- package/src/math/bankBalance.ts +0 -112
- package/src/math/conversion.js +0 -11
- package/src/math/funding.js +0 -248
- package/src/math/oracles.js +0 -26
- package/src/math/repeg.js +0 -128
- package/src/math/state.js +0 -15
- package/src/math/state.ts +0 -14
- package/src/math/trade.js +0 -253
- package/src/math/utils.js +0 -26
- package/src/math/utils.js.map +0 -1
- package/src/mockUSDCFaucet.js +0 -280
- package/src/oracles/oracleClientCache.js +0 -19
- package/src/oracles/pythClient.js +0 -46
- package/src/oracles/quoteAssetOracleClient.js +0 -32
- package/src/oracles/switchboardClient.js +0 -69
- package/src/orderParams.js +0 -20
- package/src/orders.ts +0 -245
- package/src/slot/SlotSubscriber.js +0 -39
- package/src/tokenFaucet.js +0 -189
- package/src/types.js +0 -125
- package/src/userName.js +0 -20
- package/src/wallet.js +0 -35
|
@@ -0,0 +1,4586 @@
|
|
|
1
|
+
import { expect } from 'chai';
|
|
2
|
+
import { PublicKey, Keypair } from '@solana/web3.js';
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
getVariant,
|
|
6
|
+
MarketType,
|
|
7
|
+
DLOB,
|
|
8
|
+
BN,
|
|
9
|
+
BASE_PRECISION,
|
|
10
|
+
PositionDirection,
|
|
11
|
+
OrderStatus,
|
|
12
|
+
OrderType,
|
|
13
|
+
OrderTriggerCondition,
|
|
14
|
+
PRICE_PRECISION,
|
|
15
|
+
DLOBNode,
|
|
16
|
+
OraclePriceData,
|
|
17
|
+
NodeToFill,
|
|
18
|
+
isOrderExpired,
|
|
19
|
+
Order,
|
|
20
|
+
isMarketOrder,
|
|
21
|
+
isLimitOrder,
|
|
22
|
+
ZERO,
|
|
23
|
+
} from '../../src';
|
|
24
|
+
|
|
25
|
+
import {
|
|
26
|
+
mockPerpMarkets,
|
|
27
|
+
mockSpotMarkets,
|
|
28
|
+
mockStateAccount,
|
|
29
|
+
MockUserMap,
|
|
30
|
+
} from './helpers';
|
|
31
|
+
|
|
32
|
+
function insertOrderToDLOB(
|
|
33
|
+
dlob: DLOB,
|
|
34
|
+
userAccount: PublicKey,
|
|
35
|
+
orderType: OrderType,
|
|
36
|
+
marketType: MarketType,
|
|
37
|
+
orderId: number,
|
|
38
|
+
marketIndex: number,
|
|
39
|
+
price: BN,
|
|
40
|
+
baseAssetAmount: BN,
|
|
41
|
+
direction: PositionDirection,
|
|
42
|
+
auctionStartPrice: BN,
|
|
43
|
+
auctionEndPrice: BN,
|
|
44
|
+
slot?: BN,
|
|
45
|
+
maxTs = ZERO,
|
|
46
|
+
oraclePriceOffset = new BN(0)
|
|
47
|
+
) {
|
|
48
|
+
dlob.insertOrder(
|
|
49
|
+
{
|
|
50
|
+
status: OrderStatus.OPEN,
|
|
51
|
+
orderType,
|
|
52
|
+
marketType,
|
|
53
|
+
slot: slot || new BN(1),
|
|
54
|
+
orderId,
|
|
55
|
+
userOrderId: 0,
|
|
56
|
+
marketIndex,
|
|
57
|
+
price,
|
|
58
|
+
baseAssetAmount,
|
|
59
|
+
baseAssetAmountFilled: new BN(0),
|
|
60
|
+
quoteAssetAmount: new BN(0),
|
|
61
|
+
quoteAssetAmountFilled: new BN(0),
|
|
62
|
+
direction,
|
|
63
|
+
reduceOnly: false,
|
|
64
|
+
triggerPrice: new BN(0),
|
|
65
|
+
triggerCondition: OrderTriggerCondition.ABOVE,
|
|
66
|
+
existingPositionDirection: PositionDirection.LONG,
|
|
67
|
+
postOnly: false,
|
|
68
|
+
immediateOrCancel: false,
|
|
69
|
+
oraclePriceOffset: oraclePriceOffset.toNumber(),
|
|
70
|
+
auctionDuration: 10,
|
|
71
|
+
auctionStartPrice,
|
|
72
|
+
auctionEndPrice,
|
|
73
|
+
maxTs,
|
|
74
|
+
},
|
|
75
|
+
userAccount
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function insertTriggerOrderToDLOB(
|
|
80
|
+
dlob: DLOB,
|
|
81
|
+
userAccount: PublicKey,
|
|
82
|
+
orderType: OrderType,
|
|
83
|
+
marketType: MarketType,
|
|
84
|
+
orderId: number,
|
|
85
|
+
marketIndex: number,
|
|
86
|
+
price: BN,
|
|
87
|
+
baseAssetAmount: BN,
|
|
88
|
+
direction: PositionDirection,
|
|
89
|
+
triggerPrice: BN,
|
|
90
|
+
triggerCondition: OrderTriggerCondition,
|
|
91
|
+
auctionStartPrice: BN,
|
|
92
|
+
auctionEndPrice: BN,
|
|
93
|
+
slot?: BN,
|
|
94
|
+
maxTs = ZERO,
|
|
95
|
+
oraclePriceOffset = new BN(0)
|
|
96
|
+
) {
|
|
97
|
+
dlob.insertOrder(
|
|
98
|
+
{
|
|
99
|
+
status: OrderStatus.OPEN,
|
|
100
|
+
orderType,
|
|
101
|
+
marketType,
|
|
102
|
+
slot: slot || new BN(1),
|
|
103
|
+
orderId,
|
|
104
|
+
userOrderId: 0,
|
|
105
|
+
marketIndex,
|
|
106
|
+
price,
|
|
107
|
+
baseAssetAmount,
|
|
108
|
+
baseAssetAmountFilled: new BN(0),
|
|
109
|
+
quoteAssetAmount: new BN(0),
|
|
110
|
+
quoteAssetAmountFilled: new BN(0),
|
|
111
|
+
direction,
|
|
112
|
+
reduceOnly: false,
|
|
113
|
+
triggerPrice,
|
|
114
|
+
triggerCondition,
|
|
115
|
+
existingPositionDirection: PositionDirection.LONG,
|
|
116
|
+
postOnly: false,
|
|
117
|
+
immediateOrCancel: true,
|
|
118
|
+
oraclePriceOffset: oraclePriceOffset.toNumber(),
|
|
119
|
+
auctionDuration: 10,
|
|
120
|
+
auctionStartPrice,
|
|
121
|
+
auctionEndPrice,
|
|
122
|
+
maxTs,
|
|
123
|
+
},
|
|
124
|
+
userAccount
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function printOrderNode(
|
|
129
|
+
node: DLOBNode,
|
|
130
|
+
oracle: OraclePriceData | undefined,
|
|
131
|
+
slot: number | undefined
|
|
132
|
+
) {
|
|
133
|
+
console.log(
|
|
134
|
+
` . vAMMNode? ${node.isVammNode()},\t${
|
|
135
|
+
node.order ? getVariant(node.order?.orderType) : '~'
|
|
136
|
+
} ${node.order ? getVariant(node.order?.direction) : '~'}\t, slot: ${
|
|
137
|
+
node.order?.slot.toString() || '~'
|
|
138
|
+
}, orderId: ${node.order?.orderId.toString() || '~'},\tnode.getPrice: ${
|
|
139
|
+
oracle ? node.getPrice(oracle, slot!) : '~'
|
|
140
|
+
}, node.price: ${node.order?.price.toString() || '~'}, priceOffset: ${
|
|
141
|
+
node.order?.oraclePriceOffset.toString() || '~'
|
|
142
|
+
} quantity: ${node.order?.baseAssetAmountFilled.toString() || '~'}/${
|
|
143
|
+
node.order?.baseAssetAmount.toString() || '~'
|
|
144
|
+
}`
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function printBookState(
|
|
149
|
+
dlob: DLOB,
|
|
150
|
+
marketIndex: number,
|
|
151
|
+
vBid: BN | undefined,
|
|
152
|
+
vAsk: BN | undefined,
|
|
153
|
+
slot: number,
|
|
154
|
+
oracle: OraclePriceData
|
|
155
|
+
) {
|
|
156
|
+
const askNodes = dlob.getAsks(
|
|
157
|
+
marketIndex,
|
|
158
|
+
vAsk,
|
|
159
|
+
slot,
|
|
160
|
+
MarketType.PERP,
|
|
161
|
+
oracle
|
|
162
|
+
);
|
|
163
|
+
let aa = 0;
|
|
164
|
+
console.log(`Oracle price: ${oracle.price.toNumber()}`);
|
|
165
|
+
console.log(`asks:`);
|
|
166
|
+
for (const a of askNodes) {
|
|
167
|
+
printOrderNode(a, oracle, slot);
|
|
168
|
+
aa++;
|
|
169
|
+
}
|
|
170
|
+
console.log(`Total ask nodes: ${aa}`);
|
|
171
|
+
|
|
172
|
+
const bidNodes = dlob.getBids(
|
|
173
|
+
marketIndex,
|
|
174
|
+
vBid,
|
|
175
|
+
slot,
|
|
176
|
+
MarketType.PERP,
|
|
177
|
+
oracle
|
|
178
|
+
);
|
|
179
|
+
let bb = 0;
|
|
180
|
+
console.log(`bids:`);
|
|
181
|
+
for (const b of bidNodes) {
|
|
182
|
+
printOrderNode(b, oracle, slot);
|
|
183
|
+
bb++;
|
|
184
|
+
}
|
|
185
|
+
console.log(`Total bid nodes: ${bb}`);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function printCrossedNodes(n: NodeToFill, slot: number) {
|
|
189
|
+
console.log(
|
|
190
|
+
`Cross Found, takerExists: ${n.node.order !== undefined}, makerExists: ${
|
|
191
|
+
n.makerNode !== undefined
|
|
192
|
+
}`
|
|
193
|
+
);
|
|
194
|
+
console.log(
|
|
195
|
+
`node: (mkt: ${isMarketOrder(n.node.order!)}, lim: ${isLimitOrder(
|
|
196
|
+
n.node.order!
|
|
197
|
+
)})`
|
|
198
|
+
);
|
|
199
|
+
if (n.makerNode) {
|
|
200
|
+
console.log(
|
|
201
|
+
`mkrnode: (mkt: ${isMarketOrder(n.makerNode.order!)}, lim: ${isLimitOrder(
|
|
202
|
+
n.makerNode.order!
|
|
203
|
+
)})`
|
|
204
|
+
);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const printOrder = (o: Order) => {
|
|
208
|
+
console.log(
|
|
209
|
+
` orderId: ${o.orderId}, ${getVariant(o.orderType)}, ${getVariant(
|
|
210
|
+
o.direction
|
|
211
|
+
)},\texpired: ${isOrderExpired(o, slot)}, postOnly: ${
|
|
212
|
+
o.postOnly
|
|
213
|
+
}, reduceOnly: ${
|
|
214
|
+
o.reduceOnly
|
|
215
|
+
}, price: ${o.price.toString()}, priceOffset: ${o.oraclePriceOffset.toString()}, baseAmtFileld: ${o.baseAssetAmountFilled.toString()}/${o.baseAssetAmount.toString()}`
|
|
216
|
+
);
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
if (n.node.order) {
|
|
220
|
+
const t = n.node.order;
|
|
221
|
+
console.log(`Taker Order:`);
|
|
222
|
+
printOrder(t);
|
|
223
|
+
}
|
|
224
|
+
if (n.makerNode) {
|
|
225
|
+
if (n.makerNode.isVammNode()) {
|
|
226
|
+
console.log(` maker is vAMM node`);
|
|
227
|
+
} else {
|
|
228
|
+
const m = n.makerNode.order!;
|
|
229
|
+
console.log(`Maker Order:`);
|
|
230
|
+
printOrder(m);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
describe('DLOB Tests', () => {
|
|
236
|
+
it('Fresh DLOB is empty', () => {
|
|
237
|
+
const mockUserMap = new MockUserMap();
|
|
238
|
+
|
|
239
|
+
const dlob = new DLOB(
|
|
240
|
+
mockPerpMarkets,
|
|
241
|
+
mockSpotMarkets,
|
|
242
|
+
mockStateAccount,
|
|
243
|
+
mockUserMap,
|
|
244
|
+
false
|
|
245
|
+
);
|
|
246
|
+
const vAsk = new BN(11);
|
|
247
|
+
const vBid = new BN(10);
|
|
248
|
+
const oracle = {
|
|
249
|
+
price: vBid.add(vAsk).div(new BN(2)),
|
|
250
|
+
slot: new BN(12),
|
|
251
|
+
confidence: new BN(1),
|
|
252
|
+
hasSufficientNumberOfDataPoints: true,
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
// check perps
|
|
256
|
+
for (const market of mockPerpMarkets) {
|
|
257
|
+
let foundAsks = 0;
|
|
258
|
+
for (const _ask of dlob.getAsks(
|
|
259
|
+
market.marketIndex,
|
|
260
|
+
vAsk,
|
|
261
|
+
0,
|
|
262
|
+
MarketType.PERP,
|
|
263
|
+
oracle
|
|
264
|
+
)) {
|
|
265
|
+
foundAsks++;
|
|
266
|
+
}
|
|
267
|
+
expect(foundAsks).to.equal(1);
|
|
268
|
+
|
|
269
|
+
let foundBids = 0;
|
|
270
|
+
for (const _bid of dlob.getBids(
|
|
271
|
+
market.marketIndex,
|
|
272
|
+
vBid,
|
|
273
|
+
0,
|
|
274
|
+
MarketType.PERP,
|
|
275
|
+
oracle
|
|
276
|
+
)) {
|
|
277
|
+
foundBids++;
|
|
278
|
+
}
|
|
279
|
+
expect(foundBids).to.equal(1);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// check spots
|
|
283
|
+
for (const market of mockSpotMarkets) {
|
|
284
|
+
let foundAsks = 0;
|
|
285
|
+
for (const _ask of dlob.getAsks(
|
|
286
|
+
market.marketIndex,
|
|
287
|
+
undefined,
|
|
288
|
+
0,
|
|
289
|
+
MarketType.SPOT,
|
|
290
|
+
oracle
|
|
291
|
+
)) {
|
|
292
|
+
foundAsks++;
|
|
293
|
+
}
|
|
294
|
+
expect(foundAsks).to.equal(0);
|
|
295
|
+
let foundBids = 0;
|
|
296
|
+
for (const _bid of dlob.getBids(
|
|
297
|
+
market.marketIndex,
|
|
298
|
+
undefined,
|
|
299
|
+
0,
|
|
300
|
+
MarketType.SPOT,
|
|
301
|
+
oracle
|
|
302
|
+
)) {
|
|
303
|
+
foundBids++;
|
|
304
|
+
}
|
|
305
|
+
expect(foundBids).to.equal(0);
|
|
306
|
+
}
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
it('Can clear DLOB', () => {
|
|
310
|
+
const vAsk = new BN(15);
|
|
311
|
+
const vBid = new BN(10);
|
|
312
|
+
|
|
313
|
+
const mockUserMap = new MockUserMap();
|
|
314
|
+
const user0 = Keypair.generate();
|
|
315
|
+
const user0Auth = Keypair.generate();
|
|
316
|
+
const user1 = Keypair.generate();
|
|
317
|
+
const user1Auth = Keypair.generate();
|
|
318
|
+
const user2 = Keypair.generate();
|
|
319
|
+
const user2Auth = Keypair.generate();
|
|
320
|
+
const user3 = Keypair.generate();
|
|
321
|
+
const user3Auth = Keypair.generate();
|
|
322
|
+
const user4 = Keypair.generate();
|
|
323
|
+
const user4Auth = Keypair.generate();
|
|
324
|
+
mockUserMap.addUserAccountAuthority(user0.publicKey, user0Auth.publicKey);
|
|
325
|
+
mockUserMap.addUserAccountAuthority(user1.publicKey, user1Auth.publicKey);
|
|
326
|
+
mockUserMap.addUserAccountAuthority(user2.publicKey, user2Auth.publicKey);
|
|
327
|
+
mockUserMap.addUserAccountAuthority(user3.publicKey, user3Auth.publicKey);
|
|
328
|
+
mockUserMap.addUserAccountAuthority(user4.publicKey, user4Auth.publicKey);
|
|
329
|
+
|
|
330
|
+
const dlob = new DLOB(
|
|
331
|
+
mockPerpMarkets,
|
|
332
|
+
mockSpotMarkets,
|
|
333
|
+
mockStateAccount,
|
|
334
|
+
mockUserMap,
|
|
335
|
+
false
|
|
336
|
+
);
|
|
337
|
+
const marketIndex = 0;
|
|
338
|
+
|
|
339
|
+
const slot = 12;
|
|
340
|
+
const oracle = {
|
|
341
|
+
price: vBid.add(vAsk).div(new BN(2)),
|
|
342
|
+
slot: new BN(slot),
|
|
343
|
+
confidence: new BN(1),
|
|
344
|
+
hasSufficientNumberOfDataPoints: true,
|
|
345
|
+
};
|
|
346
|
+
|
|
347
|
+
insertOrderToDLOB(
|
|
348
|
+
dlob,
|
|
349
|
+
user0.publicKey,
|
|
350
|
+
OrderType.LIMIT,
|
|
351
|
+
MarketType.PERP,
|
|
352
|
+
0, // orderId
|
|
353
|
+
marketIndex,
|
|
354
|
+
new BN(9), // price
|
|
355
|
+
BASE_PRECISION, // quantity
|
|
356
|
+
PositionDirection.LONG,
|
|
357
|
+
vBid,
|
|
358
|
+
vAsk
|
|
359
|
+
);
|
|
360
|
+
insertOrderToDLOB(
|
|
361
|
+
dlob,
|
|
362
|
+
user1.publicKey,
|
|
363
|
+
OrderType.LIMIT,
|
|
364
|
+
MarketType.PERP,
|
|
365
|
+
1, // orderId
|
|
366
|
+
marketIndex,
|
|
367
|
+
new BN(8), // price
|
|
368
|
+
BASE_PRECISION, // quantity
|
|
369
|
+
PositionDirection.LONG,
|
|
370
|
+
vBid,
|
|
371
|
+
vAsk
|
|
372
|
+
);
|
|
373
|
+
insertOrderToDLOB(
|
|
374
|
+
dlob,
|
|
375
|
+
user2.publicKey,
|
|
376
|
+
OrderType.LIMIT,
|
|
377
|
+
MarketType.PERP,
|
|
378
|
+
2, // orderId
|
|
379
|
+
marketIndex,
|
|
380
|
+
new BN(8), // price
|
|
381
|
+
BASE_PRECISION, // quantity
|
|
382
|
+
PositionDirection.LONG,
|
|
383
|
+
vBid,
|
|
384
|
+
vAsk
|
|
385
|
+
);
|
|
386
|
+
|
|
387
|
+
const bids = dlob.getBids(
|
|
388
|
+
marketIndex,
|
|
389
|
+
undefined,
|
|
390
|
+
0,
|
|
391
|
+
MarketType.PERP,
|
|
392
|
+
oracle
|
|
393
|
+
);
|
|
394
|
+
let b = 0;
|
|
395
|
+
for (const _bid of bids) {
|
|
396
|
+
b++;
|
|
397
|
+
}
|
|
398
|
+
expect(b).to.equal(3);
|
|
399
|
+
|
|
400
|
+
dlob.clear();
|
|
401
|
+
let thrown = false;
|
|
402
|
+
try {
|
|
403
|
+
const bids1 = dlob.getBids(marketIndex, vBid, 0, MarketType.PERP, oracle);
|
|
404
|
+
bids1.next();
|
|
405
|
+
} catch (e) {
|
|
406
|
+
thrown = true;
|
|
407
|
+
}
|
|
408
|
+
expect(thrown, 'should throw after clearing').to.equal(true);
|
|
409
|
+
});
|
|
410
|
+
});
|
|
411
|
+
|
|
412
|
+
describe('DLOB Perp Tests', () => {
|
|
413
|
+
it('Test proper bids', () => {
|
|
414
|
+
const vAsk = new BN(15);
|
|
415
|
+
const vBid = new BN(10);
|
|
416
|
+
const mockUserMap = new MockUserMap();
|
|
417
|
+
const dlob = new DLOB(
|
|
418
|
+
mockPerpMarkets,
|
|
419
|
+
mockSpotMarkets,
|
|
420
|
+
mockStateAccount,
|
|
421
|
+
mockUserMap,
|
|
422
|
+
false
|
|
423
|
+
);
|
|
424
|
+
const marketIndex = 0;
|
|
425
|
+
|
|
426
|
+
const slot = 12;
|
|
427
|
+
const oracle = {
|
|
428
|
+
price: vBid.add(vAsk).div(new BN(2)),
|
|
429
|
+
slot: new BN(slot),
|
|
430
|
+
confidence: new BN(1),
|
|
431
|
+
hasSufficientNumberOfDataPoints: true,
|
|
432
|
+
};
|
|
433
|
+
const testCases = [
|
|
434
|
+
{
|
|
435
|
+
expectedIdx: 0,
|
|
436
|
+
isVamm: false,
|
|
437
|
+
orderId: 5,
|
|
438
|
+
price: new BN(0),
|
|
439
|
+
direction: PositionDirection.LONG,
|
|
440
|
+
orderType: OrderType.MARKET,
|
|
441
|
+
},
|
|
442
|
+
{
|
|
443
|
+
expectedIdx: 1,
|
|
444
|
+
isVamm: false,
|
|
445
|
+
orderId: 6,
|
|
446
|
+
price: new BN(0),
|
|
447
|
+
direction: PositionDirection.LONG,
|
|
448
|
+
orderType: OrderType.MARKET,
|
|
449
|
+
},
|
|
450
|
+
{
|
|
451
|
+
expectedIdx: 2,
|
|
452
|
+
isVamm: false,
|
|
453
|
+
orderId: 7,
|
|
454
|
+
price: new BN(0),
|
|
455
|
+
direction: PositionDirection.LONG,
|
|
456
|
+
orderType: OrderType.MARKET,
|
|
457
|
+
},
|
|
458
|
+
{
|
|
459
|
+
expectedIdx: 3,
|
|
460
|
+
isVamm: false,
|
|
461
|
+
orderId: 1,
|
|
462
|
+
price: new BN(12),
|
|
463
|
+
direction: PositionDirection.LONG,
|
|
464
|
+
orderType: OrderType.LIMIT,
|
|
465
|
+
},
|
|
466
|
+
{
|
|
467
|
+
expectedIdx: 4,
|
|
468
|
+
isVamm: false,
|
|
469
|
+
orderId: 2,
|
|
470
|
+
price: new BN(11),
|
|
471
|
+
direction: PositionDirection.LONG,
|
|
472
|
+
orderType: OrderType.LIMIT,
|
|
473
|
+
},
|
|
474
|
+
{
|
|
475
|
+
expectedIdx: 7,
|
|
476
|
+
isVamm: false,
|
|
477
|
+
orderId: 3,
|
|
478
|
+
price: new BN(8),
|
|
479
|
+
direction: PositionDirection.LONG,
|
|
480
|
+
orderType: OrderType.LIMIT,
|
|
481
|
+
},
|
|
482
|
+
{
|
|
483
|
+
expectedIdx: 5,
|
|
484
|
+
isVamm: true,
|
|
485
|
+
orderId: undefined,
|
|
486
|
+
price: undefined,
|
|
487
|
+
direction: undefined,
|
|
488
|
+
orderType: undefined,
|
|
489
|
+
},
|
|
490
|
+
{
|
|
491
|
+
expectedIdx: 6,
|
|
492
|
+
isVamm: false,
|
|
493
|
+
orderId: 4,
|
|
494
|
+
price: new BN(9),
|
|
495
|
+
direction: PositionDirection.LONG,
|
|
496
|
+
orderType: OrderType.LIMIT,
|
|
497
|
+
},
|
|
498
|
+
];
|
|
499
|
+
|
|
500
|
+
for (const t of testCases) {
|
|
501
|
+
if (t.isVamm) {
|
|
502
|
+
continue;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
const user = Keypair.generate();
|
|
506
|
+
const userAuth = Keypair.generate();
|
|
507
|
+
mockUserMap.addUserAccountAuthority(user.publicKey, userAuth.publicKey);
|
|
508
|
+
|
|
509
|
+
insertOrderToDLOB(
|
|
510
|
+
dlob,
|
|
511
|
+
user.publicKey,
|
|
512
|
+
t.orderType || OrderType.LIMIT,
|
|
513
|
+
MarketType.PERP,
|
|
514
|
+
t.orderId || 0, // orderId
|
|
515
|
+
marketIndex,
|
|
516
|
+
t.price || new BN(0), // price
|
|
517
|
+
BASE_PRECISION, // quantity
|
|
518
|
+
t.direction || PositionDirection.LONG,
|
|
519
|
+
vBid,
|
|
520
|
+
vAsk
|
|
521
|
+
);
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
const expectedTestCase = testCases.sort((a, b) => {
|
|
525
|
+
// ascending order
|
|
526
|
+
return a.expectedIdx - b.expectedIdx;
|
|
527
|
+
});
|
|
528
|
+
const bids = dlob.getBids(marketIndex, vBid, slot, MarketType.PERP, oracle);
|
|
529
|
+
|
|
530
|
+
console.log('The Book Bids:');
|
|
531
|
+
const gotBids: Array<DLOBNode> = [];
|
|
532
|
+
let countBids = 0;
|
|
533
|
+
for (const bid of bids) {
|
|
534
|
+
gotBids.push(bid);
|
|
535
|
+
printOrderNode(bid, oracle, slot);
|
|
536
|
+
}
|
|
537
|
+
for (const bid of gotBids) {
|
|
538
|
+
expect(bid.isVammNode(), `expected vAMM node`).to.be.eq(
|
|
539
|
+
expectedTestCase[countBids].isVamm
|
|
540
|
+
);
|
|
541
|
+
expect(bid.order?.orderId, `expected orderId`).to.equal(
|
|
542
|
+
expectedTestCase[countBids].orderId
|
|
543
|
+
);
|
|
544
|
+
expect(bid.order?.price.toNumber(), `expected price`).to.equal(
|
|
545
|
+
expectedTestCase[countBids].price?.toNumber()
|
|
546
|
+
);
|
|
547
|
+
expect(bid.order?.direction, `expected order direction`).to.equal(
|
|
548
|
+
expectedTestCase[countBids].direction
|
|
549
|
+
);
|
|
550
|
+
expect(bid.order?.orderType, `expected order type`).to.equal(
|
|
551
|
+
expectedTestCase[countBids].orderType
|
|
552
|
+
);
|
|
553
|
+
countBids++;
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
expect(countBids).to.equal(testCases.length);
|
|
557
|
+
});
|
|
558
|
+
|
|
559
|
+
it('Test proper bids on multiple markets', () => {
|
|
560
|
+
const vAsk = new BN(15);
|
|
561
|
+
const vBid = new BN(10);
|
|
562
|
+
const mockUserMap = new MockUserMap();
|
|
563
|
+
const dlob = new DLOB(
|
|
564
|
+
mockPerpMarkets,
|
|
565
|
+
mockSpotMarkets,
|
|
566
|
+
mockStateAccount,
|
|
567
|
+
mockUserMap,
|
|
568
|
+
false
|
|
569
|
+
);
|
|
570
|
+
const marketIndex0 = 0;
|
|
571
|
+
const marketIndex1 = 1;
|
|
572
|
+
|
|
573
|
+
const slot = 12;
|
|
574
|
+
const oracle = {
|
|
575
|
+
price: vBid.add(vAsk).div(new BN(2)),
|
|
576
|
+
slot: new BN(slot),
|
|
577
|
+
confidence: new BN(1),
|
|
578
|
+
hasSufficientNumberOfDataPoints: true,
|
|
579
|
+
};
|
|
580
|
+
const testCases = [
|
|
581
|
+
{
|
|
582
|
+
expectedIdx: 3,
|
|
583
|
+
isVamm: false,
|
|
584
|
+
orderId: 5,
|
|
585
|
+
price: new BN(0),
|
|
586
|
+
direction: PositionDirection.LONG,
|
|
587
|
+
orderType: OrderType.MARKET,
|
|
588
|
+
marketIndex: marketIndex0,
|
|
589
|
+
},
|
|
590
|
+
{
|
|
591
|
+
expectedIdx: 4,
|
|
592
|
+
isVamm: false,
|
|
593
|
+
orderId: 6,
|
|
594
|
+
price: new BN(0),
|
|
595
|
+
direction: PositionDirection.LONG,
|
|
596
|
+
orderType: OrderType.MARKET,
|
|
597
|
+
marketIndex: marketIndex0,
|
|
598
|
+
},
|
|
599
|
+
{
|
|
600
|
+
expectedIdx: 5,
|
|
601
|
+
isVamm: false,
|
|
602
|
+
orderId: 7,
|
|
603
|
+
price: new BN(0),
|
|
604
|
+
direction: PositionDirection.LONG,
|
|
605
|
+
orderType: OrderType.MARKET,
|
|
606
|
+
marketIndex: marketIndex1,
|
|
607
|
+
},
|
|
608
|
+
{
|
|
609
|
+
expectedIdx: 0,
|
|
610
|
+
isVamm: false,
|
|
611
|
+
orderId: 1,
|
|
612
|
+
price: new BN(12),
|
|
613
|
+
direction: PositionDirection.LONG,
|
|
614
|
+
orderType: OrderType.LIMIT,
|
|
615
|
+
marketIndex: marketIndex0,
|
|
616
|
+
},
|
|
617
|
+
{
|
|
618
|
+
expectedIdx: 1,
|
|
619
|
+
isVamm: false,
|
|
620
|
+
orderId: 2,
|
|
621
|
+
price: new BN(11),
|
|
622
|
+
direction: PositionDirection.LONG,
|
|
623
|
+
orderType: OrderType.LIMIT,
|
|
624
|
+
marketIndex: marketIndex0,
|
|
625
|
+
},
|
|
626
|
+
{
|
|
627
|
+
expectedIdx: 7,
|
|
628
|
+
isVamm: false,
|
|
629
|
+
orderId: 3,
|
|
630
|
+
price: new BN(8),
|
|
631
|
+
direction: PositionDirection.LONG,
|
|
632
|
+
orderType: OrderType.LIMIT,
|
|
633
|
+
marketIndex: marketIndex0,
|
|
634
|
+
},
|
|
635
|
+
{
|
|
636
|
+
expectedIdx: 6,
|
|
637
|
+
isVamm: false,
|
|
638
|
+
orderId: 4,
|
|
639
|
+
price: new BN(9),
|
|
640
|
+
direction: PositionDirection.LONG,
|
|
641
|
+
orderType: OrderType.LIMIT,
|
|
642
|
+
marketIndex: marketIndex1,
|
|
643
|
+
},
|
|
644
|
+
];
|
|
645
|
+
|
|
646
|
+
for (const t of testCases) {
|
|
647
|
+
if (t.isVamm) {
|
|
648
|
+
continue;
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
const user = Keypair.generate();
|
|
652
|
+
const userAuth = Keypair.generate();
|
|
653
|
+
mockUserMap.addUserAccountAuthority(user.publicKey, userAuth.publicKey);
|
|
654
|
+
|
|
655
|
+
insertOrderToDLOB(
|
|
656
|
+
dlob,
|
|
657
|
+
user.publicKey,
|
|
658
|
+
t.orderType || OrderType.LIMIT,
|
|
659
|
+
MarketType.PERP,
|
|
660
|
+
t.orderId || 0, // orderId
|
|
661
|
+
t.marketIndex,
|
|
662
|
+
t.price || new BN(0), // price
|
|
663
|
+
BASE_PRECISION, // quantity
|
|
664
|
+
t.direction || PositionDirection.LONG,
|
|
665
|
+
vBid,
|
|
666
|
+
vAsk
|
|
667
|
+
);
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
const bids0 = dlob.getBids(
|
|
671
|
+
marketIndex0,
|
|
672
|
+
vBid,
|
|
673
|
+
slot,
|
|
674
|
+
MarketType.PERP,
|
|
675
|
+
oracle
|
|
676
|
+
);
|
|
677
|
+
let countBids0 = 0;
|
|
678
|
+
for (const bid of bids0) {
|
|
679
|
+
console.log(
|
|
680
|
+
` . vAMMNode? ${bid.isVammNode()}, ${JSON.stringify(
|
|
681
|
+
bid.order?.orderType
|
|
682
|
+
)} , ${bid.order?.orderId.toString()} , vammTestgetPRice: ${bid.getPrice(
|
|
683
|
+
oracle,
|
|
684
|
+
slot
|
|
685
|
+
)}, price: ${bid.order?.price.toString()}, quantity: ${bid.order?.baseAssetAmountFilled.toString()}/${bid.order?.baseAssetAmount.toString()}`
|
|
686
|
+
);
|
|
687
|
+
countBids0++;
|
|
688
|
+
}
|
|
689
|
+
expect(countBids0).to.equal(6);
|
|
690
|
+
|
|
691
|
+
const bids1 = dlob.getBids(
|
|
692
|
+
marketIndex1,
|
|
693
|
+
vBid,
|
|
694
|
+
slot,
|
|
695
|
+
MarketType.PERP,
|
|
696
|
+
oracle
|
|
697
|
+
);
|
|
698
|
+
let countBids1 = 0;
|
|
699
|
+
for (const bid of bids1) {
|
|
700
|
+
console.log(
|
|
701
|
+
` . vAMMNode? ${bid.isVammNode()}, ${JSON.stringify(
|
|
702
|
+
bid.order?.orderType
|
|
703
|
+
)} , ${bid.order?.orderId.toString()} , vammTestgetPRice: ${bid.getPrice(
|
|
704
|
+
oracle,
|
|
705
|
+
slot
|
|
706
|
+
)}, price: ${bid.order?.price.toString()}, quantity: ${bid.order?.baseAssetAmountFilled.toString()}/${bid.order?.baseAssetAmount.toString()}`
|
|
707
|
+
);
|
|
708
|
+
|
|
709
|
+
countBids1++;
|
|
710
|
+
}
|
|
711
|
+
expect(countBids1).to.equal(3);
|
|
712
|
+
});
|
|
713
|
+
|
|
714
|
+
it('Test proper asks', () => {
|
|
715
|
+
const vAsk = new BN(15);
|
|
716
|
+
const vBid = new BN(10);
|
|
717
|
+
const mockUserMap = new MockUserMap();
|
|
718
|
+
const dlob = new DLOB(
|
|
719
|
+
mockPerpMarkets,
|
|
720
|
+
mockSpotMarkets,
|
|
721
|
+
mockStateAccount,
|
|
722
|
+
mockUserMap,
|
|
723
|
+
false
|
|
724
|
+
);
|
|
725
|
+
const marketIndex = 0;
|
|
726
|
+
|
|
727
|
+
const slot = 12;
|
|
728
|
+
const oracle = {
|
|
729
|
+
price: vBid.add(vAsk).div(new BN(2)),
|
|
730
|
+
slot: new BN(slot),
|
|
731
|
+
confidence: new BN(1),
|
|
732
|
+
hasSufficientNumberOfDataPoints: true,
|
|
733
|
+
};
|
|
734
|
+
const testCases = [
|
|
735
|
+
{
|
|
736
|
+
expectedIdx: 0,
|
|
737
|
+
isVamm: false,
|
|
738
|
+
orderId: 3,
|
|
739
|
+
price: new BN(0),
|
|
740
|
+
direction: PositionDirection.SHORT,
|
|
741
|
+
orderType: OrderType.MARKET,
|
|
742
|
+
},
|
|
743
|
+
{
|
|
744
|
+
expectedIdx: 1,
|
|
745
|
+
isVamm: false,
|
|
746
|
+
orderId: 4,
|
|
747
|
+
price: new BN(0),
|
|
748
|
+
direction: PositionDirection.SHORT,
|
|
749
|
+
orderType: OrderType.MARKET,
|
|
750
|
+
},
|
|
751
|
+
{
|
|
752
|
+
expectedIdx: 2,
|
|
753
|
+
isVamm: false,
|
|
754
|
+
orderId: 5,
|
|
755
|
+
price: new BN(0),
|
|
756
|
+
direction: PositionDirection.SHORT,
|
|
757
|
+
orderType: OrderType.MARKET,
|
|
758
|
+
},
|
|
759
|
+
{
|
|
760
|
+
expectedIdx: 3,
|
|
761
|
+
isVamm: false,
|
|
762
|
+
orderId: 1,
|
|
763
|
+
price: new BN(13),
|
|
764
|
+
direction: PositionDirection.SHORT,
|
|
765
|
+
orderType: OrderType.LIMIT,
|
|
766
|
+
},
|
|
767
|
+
{
|
|
768
|
+
expectedIdx: 6,
|
|
769
|
+
isVamm: false,
|
|
770
|
+
orderId: 6,
|
|
771
|
+
price: new BN(16),
|
|
772
|
+
direction: PositionDirection.SHORT,
|
|
773
|
+
orderType: OrderType.LIMIT,
|
|
774
|
+
},
|
|
775
|
+
{
|
|
776
|
+
expectedIdx: 5,
|
|
777
|
+
isVamm: true,
|
|
778
|
+
orderId: undefined,
|
|
779
|
+
price: undefined,
|
|
780
|
+
direction: undefined,
|
|
781
|
+
orderType: undefined,
|
|
782
|
+
},
|
|
783
|
+
{
|
|
784
|
+
expectedIdx: 7,
|
|
785
|
+
isVamm: false,
|
|
786
|
+
orderId: 7,
|
|
787
|
+
price: new BN(17),
|
|
788
|
+
direction: PositionDirection.SHORT,
|
|
789
|
+
orderType: OrderType.LIMIT,
|
|
790
|
+
},
|
|
791
|
+
{
|
|
792
|
+
expectedIdx: 4,
|
|
793
|
+
isVamm: false,
|
|
794
|
+
orderId: 2,
|
|
795
|
+
price: new BN(14),
|
|
796
|
+
direction: PositionDirection.SHORT,
|
|
797
|
+
orderType: OrderType.LIMIT,
|
|
798
|
+
},
|
|
799
|
+
];
|
|
800
|
+
|
|
801
|
+
for (const t of testCases) {
|
|
802
|
+
if (t.isVamm) {
|
|
803
|
+
continue;
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
const user = Keypair.generate();
|
|
807
|
+
const userAuth = Keypair.generate();
|
|
808
|
+
mockUserMap.addUserAccountAuthority(user.publicKey, userAuth.publicKey);
|
|
809
|
+
|
|
810
|
+
insertOrderToDLOB(
|
|
811
|
+
dlob,
|
|
812
|
+
user.publicKey,
|
|
813
|
+
t.orderType || OrderType.LIMIT,
|
|
814
|
+
MarketType.PERP,
|
|
815
|
+
t.orderId || 0, // orderId
|
|
816
|
+
marketIndex,
|
|
817
|
+
t.price || new BN(0), // price
|
|
818
|
+
BASE_PRECISION, // quantity
|
|
819
|
+
t.direction || PositionDirection.SHORT,
|
|
820
|
+
vBid,
|
|
821
|
+
vAsk
|
|
822
|
+
);
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
const expectedTestCase = testCases.sort((a, b) => {
|
|
826
|
+
// ascending order
|
|
827
|
+
return a.expectedIdx - b.expectedIdx;
|
|
828
|
+
});
|
|
829
|
+
const asks = dlob.getAsks(marketIndex, vAsk, slot, MarketType.PERP, oracle);
|
|
830
|
+
|
|
831
|
+
console.log('The Book Asks:');
|
|
832
|
+
let countAsks = 0;
|
|
833
|
+
for (const ask of asks) {
|
|
834
|
+
console.log(
|
|
835
|
+
` . vAMMNode? ${ask.isVammNode()}, ${JSON.stringify(
|
|
836
|
+
ask.order?.orderType
|
|
837
|
+
)} , ${ask.order?.orderId.toString()} , vammTestgetPRice: ${ask.getPrice(
|
|
838
|
+
oracle,
|
|
839
|
+
slot
|
|
840
|
+
)}, price: ${ask.order?.price.toString()}, quantity: ${ask.order?.baseAssetAmountFilled.toString()}/${ask.order?.baseAssetAmount.toString()}`
|
|
841
|
+
);
|
|
842
|
+
|
|
843
|
+
expect(ask.isVammNode()).to.be.eq(expectedTestCase[countAsks].isVamm);
|
|
844
|
+
expect(ask.order?.orderId).to.equal(expectedTestCase[countAsks].orderId);
|
|
845
|
+
expect(ask.order?.price.toNumber()).to.equal(
|
|
846
|
+
expectedTestCase[countAsks].price?.toNumber()
|
|
847
|
+
);
|
|
848
|
+
expect(ask.order?.direction).to.equal(
|
|
849
|
+
expectedTestCase[countAsks].direction
|
|
850
|
+
);
|
|
851
|
+
expect(ask.order?.orderType).to.equal(
|
|
852
|
+
expectedTestCase[countAsks].orderType
|
|
853
|
+
);
|
|
854
|
+
countAsks++;
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
expect(countAsks).to.equal(testCases.length);
|
|
858
|
+
});
|
|
859
|
+
|
|
860
|
+
it('Test insert market orders', () => {
|
|
861
|
+
const vAsk = new BN(11);
|
|
862
|
+
const vBid = new BN(10);
|
|
863
|
+
const mockUserMap = new MockUserMap();
|
|
864
|
+
const dlob = new DLOB(
|
|
865
|
+
mockPerpMarkets,
|
|
866
|
+
mockSpotMarkets,
|
|
867
|
+
mockStateAccount,
|
|
868
|
+
mockUserMap,
|
|
869
|
+
false
|
|
870
|
+
);
|
|
871
|
+
const marketIndex = 0;
|
|
872
|
+
const oracle = {
|
|
873
|
+
price: vBid.add(vAsk).div(new BN(2)),
|
|
874
|
+
slot: new BN(12),
|
|
875
|
+
confidence: new BN(1),
|
|
876
|
+
hasSufficientNumberOfDataPoints: true,
|
|
877
|
+
};
|
|
878
|
+
|
|
879
|
+
// 3 mkt buys
|
|
880
|
+
for (let i = 0; i < 3; i++) {
|
|
881
|
+
const user = Keypair.generate();
|
|
882
|
+
const userAuth = Keypair.generate();
|
|
883
|
+
mockUserMap.addUserAccountAuthority(user.publicKey, userAuth.publicKey);
|
|
884
|
+
|
|
885
|
+
insertOrderToDLOB(
|
|
886
|
+
dlob,
|
|
887
|
+
user.publicKey,
|
|
888
|
+
OrderType.MARKET,
|
|
889
|
+
MarketType.PERP,
|
|
890
|
+
i + 1,
|
|
891
|
+
marketIndex,
|
|
892
|
+
new BN(0),
|
|
893
|
+
BASE_PRECISION,
|
|
894
|
+
PositionDirection.LONG,
|
|
895
|
+
vBid,
|
|
896
|
+
vAsk
|
|
897
|
+
);
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
// 3 mkt sells
|
|
901
|
+
for (let i = 0; i < 3; i++) {
|
|
902
|
+
const user = Keypair.generate();
|
|
903
|
+
const userAuth = Keypair.generate();
|
|
904
|
+
mockUserMap.addUserAccountAuthority(user.publicKey, userAuth.publicKey);
|
|
905
|
+
|
|
906
|
+
insertOrderToDLOB(
|
|
907
|
+
dlob,
|
|
908
|
+
user.publicKey,
|
|
909
|
+
OrderType.MARKET,
|
|
910
|
+
MarketType.PERP,
|
|
911
|
+
i + 1,
|
|
912
|
+
marketIndex,
|
|
913
|
+
new BN(0),
|
|
914
|
+
BASE_PRECISION,
|
|
915
|
+
PositionDirection.SHORT,
|
|
916
|
+
vBid,
|
|
917
|
+
vAsk
|
|
918
|
+
);
|
|
919
|
+
}
|
|
920
|
+
|
|
921
|
+
let asks = 0;
|
|
922
|
+
for (const ask of dlob.getAsks(
|
|
923
|
+
marketIndex,
|
|
924
|
+
vAsk,
|
|
925
|
+
2,
|
|
926
|
+
MarketType.PERP,
|
|
927
|
+
oracle
|
|
928
|
+
)) {
|
|
929
|
+
// vamm node is last in asks
|
|
930
|
+
asks++;
|
|
931
|
+
|
|
932
|
+
if (ask.order) {
|
|
933
|
+
// market orders
|
|
934
|
+
expect(getVariant(ask.order?.status)).to.equal('open');
|
|
935
|
+
expect(getVariant(ask.order?.orderType)).to.equal('market');
|
|
936
|
+
expect(getVariant(ask.order?.direction)).to.equal('short');
|
|
937
|
+
expect(ask.order?.orderId).to.equal(asks);
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
expect(asks).to.equal(4); // vamm ask + 3 orders
|
|
941
|
+
|
|
942
|
+
let bids = 0;
|
|
943
|
+
for (const bid of dlob.getBids(
|
|
944
|
+
marketIndex,
|
|
945
|
+
vBid,
|
|
946
|
+
2,
|
|
947
|
+
MarketType.PERP,
|
|
948
|
+
oracle
|
|
949
|
+
)) {
|
|
950
|
+
if (bids === 0) {
|
|
951
|
+
// vamm node
|
|
952
|
+
expect(bid.order).to.equal(undefined);
|
|
953
|
+
} else {
|
|
954
|
+
// market orders
|
|
955
|
+
expect(getVariant(bid.order?.status)).to.equal('open');
|
|
956
|
+
expect(getVariant(bid.order?.orderType)).to.equal('market');
|
|
957
|
+
expect(getVariant(bid.order?.direction)).to.equal('long');
|
|
958
|
+
expect(bid.order?.orderId).to.equal(bids);
|
|
959
|
+
}
|
|
960
|
+
bids++;
|
|
961
|
+
}
|
|
962
|
+
expect(bids).to.equal(4); // vamm bid + 3 orders
|
|
963
|
+
});
|
|
964
|
+
|
|
965
|
+
it('Test insert limit orders', () => {
|
|
966
|
+
const slot = 12;
|
|
967
|
+
const vAsk = new BN(11);
|
|
968
|
+
const vBid = new BN(10);
|
|
969
|
+
const oracle = {
|
|
970
|
+
price: vBid.add(vAsk).div(new BN(2)),
|
|
971
|
+
slot: new BN(slot),
|
|
972
|
+
confidence: new BN(1),
|
|
973
|
+
hasSufficientNumberOfDataPoints: true,
|
|
974
|
+
};
|
|
975
|
+
|
|
976
|
+
const mockUserMap = new MockUserMap();
|
|
977
|
+
const user0 = Keypair.generate();
|
|
978
|
+
const userAuth0 = Keypair.generate();
|
|
979
|
+
const user1 = Keypair.generate();
|
|
980
|
+
const userAuth1 = Keypair.generate();
|
|
981
|
+
const user2 = Keypair.generate();
|
|
982
|
+
const userAuth2 = Keypair.generate();
|
|
983
|
+
const user3 = Keypair.generate();
|
|
984
|
+
const userAuth3 = Keypair.generate();
|
|
985
|
+
const user4 = Keypair.generate();
|
|
986
|
+
const userAuth4 = Keypair.generate();
|
|
987
|
+
const user5 = Keypair.generate();
|
|
988
|
+
const userAuth5 = Keypair.generate();
|
|
989
|
+
|
|
990
|
+
mockUserMap.addUserAccountAuthority(user0.publicKey, userAuth0.publicKey);
|
|
991
|
+
mockUserMap.addUserAccountAuthority(user1.publicKey, userAuth1.publicKey);
|
|
992
|
+
mockUserMap.addUserAccountAuthority(user2.publicKey, userAuth2.publicKey);
|
|
993
|
+
mockUserMap.addUserAccountAuthority(user3.publicKey, userAuth3.publicKey);
|
|
994
|
+
mockUserMap.addUserAccountAuthority(user4.publicKey, userAuth4.publicKey);
|
|
995
|
+
mockUserMap.addUserAccountAuthority(user5.publicKey, userAuth5.publicKey);
|
|
996
|
+
|
|
997
|
+
const dlob = new DLOB(
|
|
998
|
+
mockPerpMarkets,
|
|
999
|
+
mockSpotMarkets,
|
|
1000
|
+
mockStateAccount,
|
|
1001
|
+
mockUserMap,
|
|
1002
|
+
false
|
|
1003
|
+
);
|
|
1004
|
+
const marketIndex = 0;
|
|
1005
|
+
insertOrderToDLOB(
|
|
1006
|
+
dlob,
|
|
1007
|
+
user0.publicKey,
|
|
1008
|
+
OrderType.LIMIT,
|
|
1009
|
+
MarketType.PERP,
|
|
1010
|
+
3, // orderId
|
|
1011
|
+
marketIndex,
|
|
1012
|
+
new BN(5),
|
|
1013
|
+
BASE_PRECISION,
|
|
1014
|
+
PositionDirection.LONG,
|
|
1015
|
+
vBid,
|
|
1016
|
+
vAsk
|
|
1017
|
+
);
|
|
1018
|
+
|
|
1019
|
+
insertOrderToDLOB(
|
|
1020
|
+
dlob,
|
|
1021
|
+
user1.publicKey,
|
|
1022
|
+
OrderType.LIMIT,
|
|
1023
|
+
MarketType.PERP,
|
|
1024
|
+
2,
|
|
1025
|
+
marketIndex,
|
|
1026
|
+
new BN(6),
|
|
1027
|
+
BASE_PRECISION,
|
|
1028
|
+
PositionDirection.LONG,
|
|
1029
|
+
vBid,
|
|
1030
|
+
vAsk
|
|
1031
|
+
);
|
|
1032
|
+
|
|
1033
|
+
insertOrderToDLOB(
|
|
1034
|
+
dlob,
|
|
1035
|
+
user2.publicKey,
|
|
1036
|
+
OrderType.LIMIT,
|
|
1037
|
+
MarketType.PERP,
|
|
1038
|
+
1, // orderId
|
|
1039
|
+
marketIndex,
|
|
1040
|
+
new BN(7),
|
|
1041
|
+
BASE_PRECISION,
|
|
1042
|
+
PositionDirection.LONG,
|
|
1043
|
+
vBid,
|
|
1044
|
+
vAsk
|
|
1045
|
+
);
|
|
1046
|
+
|
|
1047
|
+
insertOrderToDLOB(
|
|
1048
|
+
dlob,
|
|
1049
|
+
user3.publicKey,
|
|
1050
|
+
OrderType.LIMIT,
|
|
1051
|
+
MarketType.PERP,
|
|
1052
|
+
1, // orderId
|
|
1053
|
+
marketIndex,
|
|
1054
|
+
new BN(12),
|
|
1055
|
+
BASE_PRECISION,
|
|
1056
|
+
PositionDirection.SHORT,
|
|
1057
|
+
vBid,
|
|
1058
|
+
vAsk
|
|
1059
|
+
);
|
|
1060
|
+
|
|
1061
|
+
insertOrderToDLOB(
|
|
1062
|
+
dlob,
|
|
1063
|
+
user4.publicKey,
|
|
1064
|
+
OrderType.LIMIT,
|
|
1065
|
+
MarketType.PERP,
|
|
1066
|
+
2, // orderId
|
|
1067
|
+
marketIndex,
|
|
1068
|
+
new BN(13),
|
|
1069
|
+
BASE_PRECISION,
|
|
1070
|
+
PositionDirection.SHORT,
|
|
1071
|
+
vBid,
|
|
1072
|
+
vAsk
|
|
1073
|
+
);
|
|
1074
|
+
|
|
1075
|
+
insertOrderToDLOB(
|
|
1076
|
+
dlob,
|
|
1077
|
+
user5.publicKey,
|
|
1078
|
+
OrderType.LIMIT,
|
|
1079
|
+
MarketType.PERP,
|
|
1080
|
+
3, // orderId
|
|
1081
|
+
marketIndex,
|
|
1082
|
+
new BN(14),
|
|
1083
|
+
BASE_PRECISION,
|
|
1084
|
+
PositionDirection.SHORT,
|
|
1085
|
+
vBid,
|
|
1086
|
+
vAsk
|
|
1087
|
+
);
|
|
1088
|
+
|
|
1089
|
+
let asks = 0;
|
|
1090
|
+
for (const ask of dlob.getAsks(
|
|
1091
|
+
marketIndex,
|
|
1092
|
+
vAsk,
|
|
1093
|
+
2,
|
|
1094
|
+
MarketType.PERP,
|
|
1095
|
+
oracle
|
|
1096
|
+
)) {
|
|
1097
|
+
if (ask.order) {
|
|
1098
|
+
// market orders
|
|
1099
|
+
console.log(`ask price: ${ask.order.price.toString()}`);
|
|
1100
|
+
expect(getVariant(ask.order?.status)).to.equal('open');
|
|
1101
|
+
expect(getVariant(ask.order?.orderType)).to.equal('limit');
|
|
1102
|
+
expect(getVariant(ask.order?.direction)).to.equal('short');
|
|
1103
|
+
expect(ask.order?.orderId).to.equal(asks);
|
|
1104
|
+
expect(ask.order?.price.gt(vAsk)).to.equal(true);
|
|
1105
|
+
}
|
|
1106
|
+
|
|
1107
|
+
// vamm node is first for limit asks
|
|
1108
|
+
asks++;
|
|
1109
|
+
}
|
|
1110
|
+
expect(asks).to.equal(4); // vamm ask + 3 orders
|
|
1111
|
+
|
|
1112
|
+
let bids = 0;
|
|
1113
|
+
for (const bid of dlob.getBids(
|
|
1114
|
+
marketIndex,
|
|
1115
|
+
vBid,
|
|
1116
|
+
2,
|
|
1117
|
+
MarketType.PERP,
|
|
1118
|
+
oracle
|
|
1119
|
+
)) {
|
|
1120
|
+
if (bids === 0) {
|
|
1121
|
+
// vamm node
|
|
1122
|
+
expect(bid.order).to.equal(undefined);
|
|
1123
|
+
} else {
|
|
1124
|
+
// market orders
|
|
1125
|
+
console.log(`bid price: ${bid.order?.price.toString()}`);
|
|
1126
|
+
expect(getVariant(bid.order?.status)).to.equal('open');
|
|
1127
|
+
expect(getVariant(bid.order?.orderType)).to.equal('limit');
|
|
1128
|
+
expect(getVariant(bid.order?.direction)).to.equal('long');
|
|
1129
|
+
expect(bid.order?.orderId).to.equal(bids);
|
|
1130
|
+
expect(bid.order?.price.lt(vBid)).to.equal(true);
|
|
1131
|
+
}
|
|
1132
|
+
bids++;
|
|
1133
|
+
}
|
|
1134
|
+
expect(bids).to.equal(4); // vamm bid + 3 orders
|
|
1135
|
+
});
|
|
1136
|
+
|
|
1137
|
+
it('Test insert floatinglimit orders', () => {
|
|
1138
|
+
const slot = 12;
|
|
1139
|
+
const vAsk = new BN(11).mul(PRICE_PRECISION);
|
|
1140
|
+
const vBid = new BN(10).mul(PRICE_PRECISION);
|
|
1141
|
+
const oracle = {
|
|
1142
|
+
price: vBid.add(vAsk).div(new BN(2)),
|
|
1143
|
+
slot: new BN(slot),
|
|
1144
|
+
confidence: new BN(1),
|
|
1145
|
+
hasSufficientNumberOfDataPoints: true,
|
|
1146
|
+
};
|
|
1147
|
+
|
|
1148
|
+
const mockUserMap = new MockUserMap();
|
|
1149
|
+
const user0 = Keypair.generate();
|
|
1150
|
+
const userAuth0 = Keypair.generate();
|
|
1151
|
+
const user1 = Keypair.generate();
|
|
1152
|
+
const userAuth1 = Keypair.generate();
|
|
1153
|
+
const user2 = Keypair.generate();
|
|
1154
|
+
const userAuth2 = Keypair.generate();
|
|
1155
|
+
const user3 = Keypair.generate();
|
|
1156
|
+
const userAuth3 = Keypair.generate();
|
|
1157
|
+
const user4 = Keypair.generate();
|
|
1158
|
+
const userAuth4 = Keypair.generate();
|
|
1159
|
+
const user5 = Keypair.generate();
|
|
1160
|
+
const userAuth5 = Keypair.generate();
|
|
1161
|
+
|
|
1162
|
+
mockUserMap.addUserAccountAuthority(user0.publicKey, userAuth0.publicKey);
|
|
1163
|
+
mockUserMap.addUserAccountAuthority(user1.publicKey, userAuth1.publicKey);
|
|
1164
|
+
mockUserMap.addUserAccountAuthority(user2.publicKey, userAuth2.publicKey);
|
|
1165
|
+
mockUserMap.addUserAccountAuthority(user3.publicKey, userAuth3.publicKey);
|
|
1166
|
+
mockUserMap.addUserAccountAuthority(user4.publicKey, userAuth4.publicKey);
|
|
1167
|
+
mockUserMap.addUserAccountAuthority(user5.publicKey, userAuth5.publicKey);
|
|
1168
|
+
|
|
1169
|
+
const dlob = new DLOB(
|
|
1170
|
+
mockPerpMarkets,
|
|
1171
|
+
mockSpotMarkets,
|
|
1172
|
+
mockStateAccount,
|
|
1173
|
+
mockUserMap,
|
|
1174
|
+
false
|
|
1175
|
+
);
|
|
1176
|
+
const marketIndex = 0;
|
|
1177
|
+
|
|
1178
|
+
// insert floating bids
|
|
1179
|
+
insertOrderToDLOB(
|
|
1180
|
+
dlob,
|
|
1181
|
+
user0.publicKey,
|
|
1182
|
+
OrderType.LIMIT,
|
|
1183
|
+
MarketType.PERP,
|
|
1184
|
+
1, // orderId
|
|
1185
|
+
marketIndex,
|
|
1186
|
+
new BN(0), // price
|
|
1187
|
+
BASE_PRECISION, // quantity
|
|
1188
|
+
PositionDirection.LONG,
|
|
1189
|
+
vBid,
|
|
1190
|
+
vAsk,
|
|
1191
|
+
new BN(slot),
|
|
1192
|
+
ZERO, // TiF
|
|
1193
|
+
new BN(-1).mul(PRICE_PRECISION) // oraclePriceOffset
|
|
1194
|
+
);
|
|
1195
|
+
insertOrderToDLOB(
|
|
1196
|
+
dlob,
|
|
1197
|
+
user1.publicKey,
|
|
1198
|
+
OrderType.LIMIT,
|
|
1199
|
+
MarketType.PERP,
|
|
1200
|
+
3, // orderId
|
|
1201
|
+
marketIndex,
|
|
1202
|
+
new BN(0), // price
|
|
1203
|
+
BASE_PRECISION, // quantity
|
|
1204
|
+
PositionDirection.LONG,
|
|
1205
|
+
vBid,
|
|
1206
|
+
vAsk,
|
|
1207
|
+
new BN(slot),
|
|
1208
|
+
ZERO, // TiF
|
|
1209
|
+
new BN(-3).mul(PRICE_PRECISION) // oraclePriceOffset
|
|
1210
|
+
);
|
|
1211
|
+
insertOrderToDLOB(
|
|
1212
|
+
dlob,
|
|
1213
|
+
user2.publicKey,
|
|
1214
|
+
OrderType.LIMIT,
|
|
1215
|
+
MarketType.PERP,
|
|
1216
|
+
2, // orderId
|
|
1217
|
+
marketIndex,
|
|
1218
|
+
new BN(0), // price
|
|
1219
|
+
BASE_PRECISION, // quantity
|
|
1220
|
+
PositionDirection.LONG,
|
|
1221
|
+
vBid,
|
|
1222
|
+
vAsk,
|
|
1223
|
+
new BN(slot),
|
|
1224
|
+
ZERO, // TiF
|
|
1225
|
+
new BN(-2).mul(PRICE_PRECISION) // oraclePriceOffset
|
|
1226
|
+
);
|
|
1227
|
+
|
|
1228
|
+
// insert floating asks
|
|
1229
|
+
insertOrderToDLOB(
|
|
1230
|
+
dlob,
|
|
1231
|
+
user3.publicKey,
|
|
1232
|
+
OrderType.LIMIT,
|
|
1233
|
+
MarketType.PERP,
|
|
1234
|
+
5, // orderId
|
|
1235
|
+
marketIndex,
|
|
1236
|
+
new BN(0), // price
|
|
1237
|
+
BASE_PRECISION, // quantity
|
|
1238
|
+
PositionDirection.SHORT,
|
|
1239
|
+
vAsk,
|
|
1240
|
+
vBid,
|
|
1241
|
+
new BN(slot),
|
|
1242
|
+
ZERO, // TiF
|
|
1243
|
+
new BN(2).mul(PRICE_PRECISION) // oraclePriceOffset
|
|
1244
|
+
);
|
|
1245
|
+
insertOrderToDLOB(
|
|
1246
|
+
dlob,
|
|
1247
|
+
user4.publicKey,
|
|
1248
|
+
OrderType.LIMIT,
|
|
1249
|
+
MarketType.PERP,
|
|
1250
|
+
6, // orderId
|
|
1251
|
+
marketIndex,
|
|
1252
|
+
new BN(0), // price
|
|
1253
|
+
BASE_PRECISION, // quantity
|
|
1254
|
+
PositionDirection.SHORT,
|
|
1255
|
+
vAsk,
|
|
1256
|
+
vBid,
|
|
1257
|
+
new BN(slot),
|
|
1258
|
+
ZERO, // TiF
|
|
1259
|
+
new BN(3).mul(PRICE_PRECISION) // oraclePriceOffset
|
|
1260
|
+
);
|
|
1261
|
+
insertOrderToDLOB(
|
|
1262
|
+
dlob,
|
|
1263
|
+
user5.publicKey,
|
|
1264
|
+
OrderType.LIMIT,
|
|
1265
|
+
MarketType.PERP,
|
|
1266
|
+
4, // orderId
|
|
1267
|
+
marketIndex,
|
|
1268
|
+
new BN(0), // price
|
|
1269
|
+
BASE_PRECISION, // quantity
|
|
1270
|
+
PositionDirection.SHORT,
|
|
1271
|
+
vAsk,
|
|
1272
|
+
vBid,
|
|
1273
|
+
new BN(slot),
|
|
1274
|
+
ZERO, // TiF
|
|
1275
|
+
new BN(1).mul(PRICE_PRECISION) // oraclePriceOffset
|
|
1276
|
+
);
|
|
1277
|
+
|
|
1278
|
+
// check floating bids
|
|
1279
|
+
console.log(`bids:`);
|
|
1280
|
+
let lastBidPrice = new BN(9999999999999); // very big
|
|
1281
|
+
let bids = 0;
|
|
1282
|
+
for (const bid of dlob.getBids(
|
|
1283
|
+
marketIndex,
|
|
1284
|
+
vBid,
|
|
1285
|
+
slot,
|
|
1286
|
+
MarketType.PERP,
|
|
1287
|
+
oracle
|
|
1288
|
+
)) {
|
|
1289
|
+
printOrderNode(bid, oracle, slot);
|
|
1290
|
+
|
|
1291
|
+
if (!bid.isVammNode()) {
|
|
1292
|
+
expect(getVariant(bid.order?.status)).to.equal('open');
|
|
1293
|
+
expect(getVariant(bid.order?.orderType)).to.equal('limit');
|
|
1294
|
+
expect(getVariant(bid.order?.direction)).to.equal('long');
|
|
1295
|
+
|
|
1296
|
+
// price should be getting worse (getting lower) as we iterate
|
|
1297
|
+
const currentPrice = bid.getPrice(oracle, slot);
|
|
1298
|
+
expect(
|
|
1299
|
+
currentPrice.lte(lastBidPrice),
|
|
1300
|
+
`each bid should be lte the last. current: ${currentPrice.toString()}, last: ${lastBidPrice.toString()}`
|
|
1301
|
+
).to.be.true;
|
|
1302
|
+
}
|
|
1303
|
+
lastBidPrice = bid.getPrice(oracle, slot);
|
|
1304
|
+
bids++;
|
|
1305
|
+
}
|
|
1306
|
+
expect(bids).to.equal(4); // vamm bid + 3 orders
|
|
1307
|
+
|
|
1308
|
+
// check floating asks
|
|
1309
|
+
console.log(`asks:`);
|
|
1310
|
+
let asks = 0;
|
|
1311
|
+
for (const ask of dlob.getAsks(
|
|
1312
|
+
marketIndex,
|
|
1313
|
+
vAsk,
|
|
1314
|
+
slot,
|
|
1315
|
+
MarketType.PERP,
|
|
1316
|
+
oracle
|
|
1317
|
+
)) {
|
|
1318
|
+
printOrderNode(ask, oracle, slot);
|
|
1319
|
+
if (!ask.isVammNode()) {
|
|
1320
|
+
expect(getVariant(ask.order?.status)).to.equal('open');
|
|
1321
|
+
expect(getVariant(ask.order?.orderType)).to.equal('limit');
|
|
1322
|
+
expect(getVariant(ask.order?.direction)).to.equal('short');
|
|
1323
|
+
|
|
1324
|
+
// price should be getting worse (getting higher) as we iterate
|
|
1325
|
+
const currentPrice = ask.getPrice(oracle, slot);
|
|
1326
|
+
expect(
|
|
1327
|
+
currentPrice.gte(lastBidPrice),
|
|
1328
|
+
`each ask should be gte the last. current: ${currentPrice.toString()}, last: ${lastBidPrice.toString()}`
|
|
1329
|
+
).to.be.true;
|
|
1330
|
+
}
|
|
1331
|
+
asks++;
|
|
1332
|
+
}
|
|
1333
|
+
expect(asks).to.equal(4); // vamm ask + 3 orders
|
|
1334
|
+
});
|
|
1335
|
+
|
|
1336
|
+
it('Test multiple market orders fill with multiple limit orders', async () => {
|
|
1337
|
+
const vAsk = new BN(15);
|
|
1338
|
+
const vBid = new BN(10);
|
|
1339
|
+
|
|
1340
|
+
const mockUserMap = new MockUserMap();
|
|
1341
|
+
const user0 = Keypair.generate();
|
|
1342
|
+
const user0Auth = Keypair.generate();
|
|
1343
|
+
const user1 = Keypair.generate();
|
|
1344
|
+
const user1Auth = Keypair.generate();
|
|
1345
|
+
const user2 = Keypair.generate();
|
|
1346
|
+
const user2Auth = Keypair.generate();
|
|
1347
|
+
const user3 = Keypair.generate();
|
|
1348
|
+
const user3Auth = Keypair.generate();
|
|
1349
|
+
const user4 = Keypair.generate();
|
|
1350
|
+
const user4Auth = Keypair.generate();
|
|
1351
|
+
mockUserMap.addUserAccountAuthority(user0.publicKey, user0Auth.publicKey);
|
|
1352
|
+
mockUserMap.addUserAccountAuthority(user1.publicKey, user1Auth.publicKey);
|
|
1353
|
+
mockUserMap.addUserAccountAuthority(user2.publicKey, user2Auth.publicKey);
|
|
1354
|
+
mockUserMap.addUserAccountAuthority(user3.publicKey, user3Auth.publicKey);
|
|
1355
|
+
mockUserMap.addUserAccountAuthority(user4.publicKey, user4Auth.publicKey);
|
|
1356
|
+
|
|
1357
|
+
const dlob = new DLOB(
|
|
1358
|
+
mockPerpMarkets,
|
|
1359
|
+
mockSpotMarkets,
|
|
1360
|
+
mockStateAccount,
|
|
1361
|
+
mockUserMap,
|
|
1362
|
+
false
|
|
1363
|
+
);
|
|
1364
|
+
const marketIndex = 0;
|
|
1365
|
+
|
|
1366
|
+
// insert some limit buys above vamm bid, below ask
|
|
1367
|
+
insertOrderToDLOB(
|
|
1368
|
+
dlob,
|
|
1369
|
+
user0.publicKey,
|
|
1370
|
+
OrderType.LIMIT,
|
|
1371
|
+
MarketType.PERP,
|
|
1372
|
+
1, // orderId
|
|
1373
|
+
marketIndex,
|
|
1374
|
+
new BN(11), // price
|
|
1375
|
+
BASE_PRECISION, // quantity
|
|
1376
|
+
PositionDirection.LONG,
|
|
1377
|
+
vBid,
|
|
1378
|
+
vAsk
|
|
1379
|
+
);
|
|
1380
|
+
insertOrderToDLOB(
|
|
1381
|
+
dlob,
|
|
1382
|
+
user1.publicKey,
|
|
1383
|
+
OrderType.LIMIT,
|
|
1384
|
+
MarketType.PERP,
|
|
1385
|
+
2, // orderId
|
|
1386
|
+
marketIndex,
|
|
1387
|
+
new BN(12), // price
|
|
1388
|
+
BASE_PRECISION, // quantity
|
|
1389
|
+
PositionDirection.LONG,
|
|
1390
|
+
vBid,
|
|
1391
|
+
vAsk
|
|
1392
|
+
);
|
|
1393
|
+
insertOrderToDLOB(
|
|
1394
|
+
dlob,
|
|
1395
|
+
user2.publicKey,
|
|
1396
|
+
OrderType.LIMIT,
|
|
1397
|
+
MarketType.PERP,
|
|
1398
|
+
3, // orderId
|
|
1399
|
+
marketIndex,
|
|
1400
|
+
new BN(13), // price
|
|
1401
|
+
BASE_PRECISION, // quantity
|
|
1402
|
+
PositionDirection.LONG,
|
|
1403
|
+
vBid,
|
|
1404
|
+
vAsk
|
|
1405
|
+
);
|
|
1406
|
+
|
|
1407
|
+
// should have no crossing orders
|
|
1408
|
+
const nodesToFillBefore = dlob.findCrossingNodesToFill(
|
|
1409
|
+
marketIndex,
|
|
1410
|
+
12, // auction over
|
|
1411
|
+
MarketType.PERP,
|
|
1412
|
+
{
|
|
1413
|
+
price: vBid.add(vAsk).div(new BN(2)),
|
|
1414
|
+
slot: new BN(12),
|
|
1415
|
+
confidence: new BN(1),
|
|
1416
|
+
hasSufficientNumberOfDataPoints: true,
|
|
1417
|
+
}
|
|
1418
|
+
);
|
|
1419
|
+
expect(nodesToFillBefore.length).to.equal(0);
|
|
1420
|
+
|
|
1421
|
+
// place two market sell order eating 2 of the limit orders
|
|
1422
|
+
insertOrderToDLOB(
|
|
1423
|
+
dlob,
|
|
1424
|
+
user3.publicKey,
|
|
1425
|
+
OrderType.MARKET,
|
|
1426
|
+
MarketType.PERP,
|
|
1427
|
+
4, // orderId
|
|
1428
|
+
marketIndex,
|
|
1429
|
+
new BN(12), // price
|
|
1430
|
+
new BN(1).mul(BASE_PRECISION), // quantity
|
|
1431
|
+
PositionDirection.SHORT,
|
|
1432
|
+
vBid,
|
|
1433
|
+
vAsk
|
|
1434
|
+
);
|
|
1435
|
+
insertOrderToDLOB(
|
|
1436
|
+
dlob,
|
|
1437
|
+
user4.publicKey,
|
|
1438
|
+
OrderType.MARKET,
|
|
1439
|
+
MarketType.PERP,
|
|
1440
|
+
5, // orderId
|
|
1441
|
+
marketIndex,
|
|
1442
|
+
new BN(12), // price
|
|
1443
|
+
new BN(1).mul(BASE_PRECISION), // quantity
|
|
1444
|
+
PositionDirection.SHORT,
|
|
1445
|
+
vBid,
|
|
1446
|
+
vAsk
|
|
1447
|
+
);
|
|
1448
|
+
|
|
1449
|
+
const nodesToFillAfter = dlob.findCrossingNodesToFill(
|
|
1450
|
+
marketIndex,
|
|
1451
|
+
12, // auction over
|
|
1452
|
+
MarketType.PERP,
|
|
1453
|
+
{
|
|
1454
|
+
price: vBid.add(vAsk).div(new BN(2)),
|
|
1455
|
+
slot: new BN(12),
|
|
1456
|
+
confidence: new BN(1),
|
|
1457
|
+
hasSufficientNumberOfDataPoints: true,
|
|
1458
|
+
}
|
|
1459
|
+
);
|
|
1460
|
+
console.log(`Filled nodes: ${nodesToFillAfter.length}`);
|
|
1461
|
+
for (const n of nodesToFillAfter) {
|
|
1462
|
+
printCrossedNodes(n, 12);
|
|
1463
|
+
}
|
|
1464
|
+
expect(nodesToFillAfter.length).to.equal(2);
|
|
1465
|
+
|
|
1466
|
+
// first taker should fill with best maker
|
|
1467
|
+
expect(nodesToFillAfter[0].node.order?.orderId).to.equal(4);
|
|
1468
|
+
expect(nodesToFillAfter[0].makerNode?.order?.orderId).to.equal(3);
|
|
1469
|
+
|
|
1470
|
+
// second taker should fill with second best maker
|
|
1471
|
+
expect(nodesToFillAfter[1].node.order?.orderId).to.equal(5);
|
|
1472
|
+
expect(nodesToFillAfter[1].makerNode?.order?.orderId).to.equal(2);
|
|
1473
|
+
});
|
|
1474
|
+
|
|
1475
|
+
it('Test one market orders fills two limit orders', () => {
|
|
1476
|
+
const vAsk = new BN(15);
|
|
1477
|
+
const vBid = new BN(10);
|
|
1478
|
+
|
|
1479
|
+
const mockUserMap = new MockUserMap();
|
|
1480
|
+
const user0 = Keypair.generate();
|
|
1481
|
+
const user0Auth = Keypair.generate();
|
|
1482
|
+
const user1 = Keypair.generate();
|
|
1483
|
+
const user1Auth = Keypair.generate();
|
|
1484
|
+
const user2 = Keypair.generate();
|
|
1485
|
+
const user2Auth = Keypair.generate();
|
|
1486
|
+
const user3 = Keypair.generate();
|
|
1487
|
+
const user3Auth = Keypair.generate();
|
|
1488
|
+
mockUserMap.addUserAccountAuthority(user0.publicKey, user0Auth.publicKey);
|
|
1489
|
+
mockUserMap.addUserAccountAuthority(user1.publicKey, user1Auth.publicKey);
|
|
1490
|
+
mockUserMap.addUserAccountAuthority(user2.publicKey, user2Auth.publicKey);
|
|
1491
|
+
mockUserMap.addUserAccountAuthority(user3.publicKey, user3Auth.publicKey);
|
|
1492
|
+
|
|
1493
|
+
const dlob = new DLOB(
|
|
1494
|
+
mockPerpMarkets,
|
|
1495
|
+
mockSpotMarkets,
|
|
1496
|
+
mockStateAccount,
|
|
1497
|
+
mockUserMap,
|
|
1498
|
+
false
|
|
1499
|
+
);
|
|
1500
|
+
const marketIndex = 0;
|
|
1501
|
+
|
|
1502
|
+
// insert some limit sells below vAMM ask, above bid
|
|
1503
|
+
insertOrderToDLOB(
|
|
1504
|
+
dlob,
|
|
1505
|
+
user0.publicKey,
|
|
1506
|
+
OrderType.LIMIT,
|
|
1507
|
+
MarketType.PERP,
|
|
1508
|
+
1, // orderId
|
|
1509
|
+
marketIndex,
|
|
1510
|
+
new BN(14), // price
|
|
1511
|
+
BASE_PRECISION, // quantity
|
|
1512
|
+
PositionDirection.SHORT,
|
|
1513
|
+
vBid,
|
|
1514
|
+
vAsk
|
|
1515
|
+
);
|
|
1516
|
+
insertOrderToDLOB(
|
|
1517
|
+
dlob,
|
|
1518
|
+
user1.publicKey,
|
|
1519
|
+
OrderType.LIMIT,
|
|
1520
|
+
MarketType.PERP,
|
|
1521
|
+
2, // orderId
|
|
1522
|
+
marketIndex,
|
|
1523
|
+
new BN(13), // price
|
|
1524
|
+
BASE_PRECISION, // quantity
|
|
1525
|
+
PositionDirection.SHORT,
|
|
1526
|
+
vBid,
|
|
1527
|
+
vAsk
|
|
1528
|
+
);
|
|
1529
|
+
insertOrderToDLOB(
|
|
1530
|
+
dlob,
|
|
1531
|
+
user2.publicKey,
|
|
1532
|
+
OrderType.LIMIT,
|
|
1533
|
+
MarketType.PERP,
|
|
1534
|
+
3, // orderId
|
|
1535
|
+
marketIndex,
|
|
1536
|
+
new BN(12), // price
|
|
1537
|
+
BASE_PRECISION, // quantity
|
|
1538
|
+
PositionDirection.SHORT,
|
|
1539
|
+
vBid,
|
|
1540
|
+
vAsk
|
|
1541
|
+
);
|
|
1542
|
+
|
|
1543
|
+
// auction over
|
|
1544
|
+
const endSlot = 12;
|
|
1545
|
+
|
|
1546
|
+
// should have no crossing orders
|
|
1547
|
+
const nodesToFillBefore = dlob.findCrossingNodesToFill(
|
|
1548
|
+
marketIndex,
|
|
1549
|
+
endSlot,
|
|
1550
|
+
MarketType.PERP,
|
|
1551
|
+
{
|
|
1552
|
+
price: vBid.add(vAsk).div(new BN(2)),
|
|
1553
|
+
slot: new BN(endSlot),
|
|
1554
|
+
confidence: new BN(1),
|
|
1555
|
+
hasSufficientNumberOfDataPoints: true,
|
|
1556
|
+
}
|
|
1557
|
+
);
|
|
1558
|
+
expect(nodesToFillBefore.length).to.equal(0);
|
|
1559
|
+
|
|
1560
|
+
// place one market buy order eating 2 of the limit orders
|
|
1561
|
+
insertOrderToDLOB(
|
|
1562
|
+
dlob,
|
|
1563
|
+
user3.publicKey,
|
|
1564
|
+
OrderType.MARKET,
|
|
1565
|
+
MarketType.PERP,
|
|
1566
|
+
4, // orderId
|
|
1567
|
+
marketIndex,
|
|
1568
|
+
new BN(13), // price
|
|
1569
|
+
new BN(2).mul(BASE_PRECISION), // quantity
|
|
1570
|
+
PositionDirection.LONG,
|
|
1571
|
+
vBid,
|
|
1572
|
+
vAsk
|
|
1573
|
+
);
|
|
1574
|
+
|
|
1575
|
+
const nodesToFillAfter = dlob.findCrossingNodesToFill(
|
|
1576
|
+
marketIndex,
|
|
1577
|
+
endSlot,
|
|
1578
|
+
MarketType.PERP,
|
|
1579
|
+
{
|
|
1580
|
+
price: vBid.add(vAsk).div(new BN(2)),
|
|
1581
|
+
slot: new BN(endSlot),
|
|
1582
|
+
confidence: new BN(1),
|
|
1583
|
+
hasSufficientNumberOfDataPoints: true,
|
|
1584
|
+
}
|
|
1585
|
+
);
|
|
1586
|
+
console.log(`Filled nodes: ${nodesToFillAfter.length}`);
|
|
1587
|
+
for (const n of nodesToFillAfter) {
|
|
1588
|
+
printCrossedNodes(n, endSlot);
|
|
1589
|
+
}
|
|
1590
|
+
expect(nodesToFillAfter.length).to.equal(2);
|
|
1591
|
+
|
|
1592
|
+
// taker should fill completely with best maker
|
|
1593
|
+
expect(
|
|
1594
|
+
nodesToFillAfter[0].node.order?.orderId,
|
|
1595
|
+
'wrong taker orderId'
|
|
1596
|
+
).to.equal(4);
|
|
1597
|
+
expect(
|
|
1598
|
+
nodesToFillAfter[0].makerNode?.order?.orderId,
|
|
1599
|
+
'wrong maker orderId'
|
|
1600
|
+
).to.equal(3);
|
|
1601
|
+
|
|
1602
|
+
// taker should fill completely with second best maker
|
|
1603
|
+
expect(
|
|
1604
|
+
nodesToFillAfter[1].node.order?.orderId,
|
|
1605
|
+
'wrong taker orderId'
|
|
1606
|
+
).to.equal(4);
|
|
1607
|
+
expect(
|
|
1608
|
+
nodesToFillAfter[1].makerNode?.order?.orderId,
|
|
1609
|
+
'wrong maker orderId'
|
|
1610
|
+
).to.equal(2);
|
|
1611
|
+
});
|
|
1612
|
+
|
|
1613
|
+
it('Test two market orders to fill one limit order', () => {
|
|
1614
|
+
const vAsk = new BN(15);
|
|
1615
|
+
const vBid = new BN(8);
|
|
1616
|
+
|
|
1617
|
+
const mockUserMap = new MockUserMap();
|
|
1618
|
+
const user0 = Keypair.generate();
|
|
1619
|
+
const user0Auth = Keypair.generate();
|
|
1620
|
+
const user1 = Keypair.generate();
|
|
1621
|
+
const user1Auth = Keypair.generate();
|
|
1622
|
+
const user2 = Keypair.generate();
|
|
1623
|
+
const user2Auth = Keypair.generate();
|
|
1624
|
+
const user3 = Keypair.generate();
|
|
1625
|
+
const user3Auth = Keypair.generate();
|
|
1626
|
+
const user4 = Keypair.generate();
|
|
1627
|
+
const user4Auth = Keypair.generate();
|
|
1628
|
+
mockUserMap.addUserAccountAuthority(user0.publicKey, user0Auth.publicKey);
|
|
1629
|
+
mockUserMap.addUserAccountAuthority(user1.publicKey, user1Auth.publicKey);
|
|
1630
|
+
mockUserMap.addUserAccountAuthority(user2.publicKey, user2Auth.publicKey);
|
|
1631
|
+
mockUserMap.addUserAccountAuthority(user3.publicKey, user3Auth.publicKey);
|
|
1632
|
+
mockUserMap.addUserAccountAuthority(user4.publicKey, user4Auth.publicKey);
|
|
1633
|
+
|
|
1634
|
+
const dlob = new DLOB(
|
|
1635
|
+
mockPerpMarkets,
|
|
1636
|
+
mockSpotMarkets,
|
|
1637
|
+
mockStateAccount,
|
|
1638
|
+
mockUserMap,
|
|
1639
|
+
false
|
|
1640
|
+
);
|
|
1641
|
+
const marketIndex = 0;
|
|
1642
|
+
|
|
1643
|
+
const slot = 12;
|
|
1644
|
+
const oracle = {
|
|
1645
|
+
price: vBid.add(vAsk).div(new BN(2)),
|
|
1646
|
+
slot: new BN(slot),
|
|
1647
|
+
confidence: new BN(1),
|
|
1648
|
+
hasSufficientNumberOfDataPoints: true,
|
|
1649
|
+
};
|
|
1650
|
+
|
|
1651
|
+
// insert some limit sells below vAMM ask, above bid
|
|
1652
|
+
insertOrderToDLOB(
|
|
1653
|
+
dlob,
|
|
1654
|
+
user0.publicKey,
|
|
1655
|
+
OrderType.LIMIT,
|
|
1656
|
+
MarketType.PERP,
|
|
1657
|
+
1, // orderId
|
|
1658
|
+
marketIndex,
|
|
1659
|
+
new BN(14), // price
|
|
1660
|
+
BASE_PRECISION, // quantity
|
|
1661
|
+
PositionDirection.SHORT,
|
|
1662
|
+
vBid,
|
|
1663
|
+
vAsk
|
|
1664
|
+
);
|
|
1665
|
+
insertOrderToDLOB(
|
|
1666
|
+
dlob,
|
|
1667
|
+
user1.publicKey,
|
|
1668
|
+
OrderType.LIMIT,
|
|
1669
|
+
MarketType.PERP,
|
|
1670
|
+
2, // orderId
|
|
1671
|
+
marketIndex,
|
|
1672
|
+
new BN(13), // price
|
|
1673
|
+
BASE_PRECISION, // quantity
|
|
1674
|
+
PositionDirection.SHORT,
|
|
1675
|
+
vBid,
|
|
1676
|
+
vAsk
|
|
1677
|
+
);
|
|
1678
|
+
insertOrderToDLOB(
|
|
1679
|
+
dlob,
|
|
1680
|
+
user2.publicKey,
|
|
1681
|
+
OrderType.LIMIT,
|
|
1682
|
+
MarketType.PERP,
|
|
1683
|
+
3, // orderId
|
|
1684
|
+
marketIndex,
|
|
1685
|
+
new BN(9), // price <-- best price
|
|
1686
|
+
new BN(3).mul(BASE_PRECISION), // quantity
|
|
1687
|
+
PositionDirection.SHORT,
|
|
1688
|
+
vBid,
|
|
1689
|
+
vAsk
|
|
1690
|
+
);
|
|
1691
|
+
|
|
1692
|
+
// should have no crossing orders
|
|
1693
|
+
const nodesToFillBefore = dlob.findCrossingNodesToFill(
|
|
1694
|
+
marketIndex,
|
|
1695
|
+
12, // auction over
|
|
1696
|
+
MarketType.PERP,
|
|
1697
|
+
oracle
|
|
1698
|
+
);
|
|
1699
|
+
expect(nodesToFillBefore.length).to.equal(0);
|
|
1700
|
+
|
|
1701
|
+
// place two market buy orders to eat the best ask
|
|
1702
|
+
insertOrderToDLOB(
|
|
1703
|
+
dlob,
|
|
1704
|
+
user3.publicKey,
|
|
1705
|
+
OrderType.MARKET,
|
|
1706
|
+
MarketType.PERP,
|
|
1707
|
+
4, // orderId
|
|
1708
|
+
marketIndex,
|
|
1709
|
+
new BN(0), // price
|
|
1710
|
+
new BN(1).mul(BASE_PRECISION), // quantity
|
|
1711
|
+
PositionDirection.LONG,
|
|
1712
|
+
vBid,
|
|
1713
|
+
vAsk
|
|
1714
|
+
);
|
|
1715
|
+
insertOrderToDLOB(
|
|
1716
|
+
dlob,
|
|
1717
|
+
user4.publicKey,
|
|
1718
|
+
OrderType.MARKET,
|
|
1719
|
+
MarketType.PERP,
|
|
1720
|
+
5, // orderId
|
|
1721
|
+
marketIndex,
|
|
1722
|
+
new BN(0), // price
|
|
1723
|
+
new BN(2).mul(BASE_PRECISION), // quantity
|
|
1724
|
+
PositionDirection.LONG,
|
|
1725
|
+
vBid,
|
|
1726
|
+
vAsk
|
|
1727
|
+
);
|
|
1728
|
+
|
|
1729
|
+
const nodesToFillAfter = dlob.findCrossingNodesToFill(
|
|
1730
|
+
marketIndex,
|
|
1731
|
+
slot, // auction over
|
|
1732
|
+
MarketType.PERP,
|
|
1733
|
+
oracle
|
|
1734
|
+
);
|
|
1735
|
+
const mktNodes = dlob.findExpiredNodesToFill(
|
|
1736
|
+
marketIndex,
|
|
1737
|
+
slot,
|
|
1738
|
+
MarketType.PERP
|
|
1739
|
+
);
|
|
1740
|
+
console.log(`market nodes: ${mktNodes.length}`);
|
|
1741
|
+
|
|
1742
|
+
printBookState(dlob, marketIndex, vBid, vAsk, slot, oracle);
|
|
1743
|
+
|
|
1744
|
+
console.log(`Filled nodes: ${nodesToFillAfter.length}`);
|
|
1745
|
+
for (const n of nodesToFillAfter) {
|
|
1746
|
+
printCrossedNodes(n, slot);
|
|
1747
|
+
}
|
|
1748
|
+
expect(nodesToFillAfter.length).to.equal(2);
|
|
1749
|
+
|
|
1750
|
+
// taker should fill completely with best maker
|
|
1751
|
+
expect(nodesToFillAfter[0].node.order?.orderId).to.equal(4);
|
|
1752
|
+
expect(nodesToFillAfter[0].makerNode?.order?.orderId).to.equal(3);
|
|
1753
|
+
|
|
1754
|
+
// taker should fill completely with second best maker
|
|
1755
|
+
expect(nodesToFillAfter[1].node.order?.orderId).to.equal(5);
|
|
1756
|
+
expect(nodesToFillAfter[1].makerNode?.order?.orderId).to.equal(3);
|
|
1757
|
+
});
|
|
1758
|
+
|
|
1759
|
+
it('Test trigger orders', () => {
|
|
1760
|
+
const vAsk = new BN(15);
|
|
1761
|
+
const vBid = new BN(8);
|
|
1762
|
+
|
|
1763
|
+
const mockUserMap = new MockUserMap();
|
|
1764
|
+
const user0 = Keypair.generate();
|
|
1765
|
+
const user0Auth = Keypair.generate();
|
|
1766
|
+
const user1 = Keypair.generate();
|
|
1767
|
+
const user1Auth = Keypair.generate();
|
|
1768
|
+
const user2 = Keypair.generate();
|
|
1769
|
+
const user2Auth = Keypair.generate();
|
|
1770
|
+
const user3 = Keypair.generate();
|
|
1771
|
+
const user3Auth = Keypair.generate();
|
|
1772
|
+
const user4 = Keypair.generate();
|
|
1773
|
+
const user4Auth = Keypair.generate();
|
|
1774
|
+
const user5 = Keypair.generate();
|
|
1775
|
+
const user5Auth = Keypair.generate();
|
|
1776
|
+
const user6 = Keypair.generate();
|
|
1777
|
+
const user6Auth = Keypair.generate();
|
|
1778
|
+
const user7 = Keypair.generate();
|
|
1779
|
+
const user7Auth = Keypair.generate();
|
|
1780
|
+
const user8 = Keypair.generate();
|
|
1781
|
+
const user8Auth = Keypair.generate();
|
|
1782
|
+
const user9 = Keypair.generate();
|
|
1783
|
+
const user9Auth = Keypair.generate();
|
|
1784
|
+
const user10 = Keypair.generate();
|
|
1785
|
+
const user10Auth = Keypair.generate();
|
|
1786
|
+
const user11 = Keypair.generate();
|
|
1787
|
+
const user11Auth = Keypair.generate();
|
|
1788
|
+
mockUserMap.addUserAccountAuthority(user0.publicKey, user0Auth.publicKey);
|
|
1789
|
+
mockUserMap.addUserAccountAuthority(user1.publicKey, user1Auth.publicKey);
|
|
1790
|
+
mockUserMap.addUserAccountAuthority(user2.publicKey, user2Auth.publicKey);
|
|
1791
|
+
mockUserMap.addUserAccountAuthority(user3.publicKey, user3Auth.publicKey);
|
|
1792
|
+
mockUserMap.addUserAccountAuthority(user4.publicKey, user4Auth.publicKey);
|
|
1793
|
+
mockUserMap.addUserAccountAuthority(user5.publicKey, user5Auth.publicKey);
|
|
1794
|
+
mockUserMap.addUserAccountAuthority(user6.publicKey, user6Auth.publicKey);
|
|
1795
|
+
mockUserMap.addUserAccountAuthority(user7.publicKey, user7Auth.publicKey);
|
|
1796
|
+
mockUserMap.addUserAccountAuthority(user8.publicKey, user8Auth.publicKey);
|
|
1797
|
+
mockUserMap.addUserAccountAuthority(user9.publicKey, user9Auth.publicKey);
|
|
1798
|
+
mockUserMap.addUserAccountAuthority(user10.publicKey, user10Auth.publicKey);
|
|
1799
|
+
mockUserMap.addUserAccountAuthority(user11.publicKey, user11Auth.publicKey);
|
|
1800
|
+
|
|
1801
|
+
const dlob = new DLOB(
|
|
1802
|
+
mockPerpMarkets,
|
|
1803
|
+
mockSpotMarkets,
|
|
1804
|
+
mockStateAccount,
|
|
1805
|
+
mockUserMap,
|
|
1806
|
+
false
|
|
1807
|
+
);
|
|
1808
|
+
const marketIndex = 0;
|
|
1809
|
+
|
|
1810
|
+
const slot = 20;
|
|
1811
|
+
const oracle = {
|
|
1812
|
+
price: vBid.add(vAsk).div(new BN(2)),
|
|
1813
|
+
slot: new BN(slot),
|
|
1814
|
+
confidence: new BN(1),
|
|
1815
|
+
hasSufficientNumberOfDataPoints: true,
|
|
1816
|
+
};
|
|
1817
|
+
|
|
1818
|
+
const orderIdsToTrigger = [1, 2, 3, 4, 5, 6, 7, 8];
|
|
1819
|
+
// const orderIdsToNotTrigger = [9, 10, 11, 12];
|
|
1820
|
+
|
|
1821
|
+
// should trigger limit buy with above condition
|
|
1822
|
+
insertTriggerOrderToDLOB(
|
|
1823
|
+
dlob,
|
|
1824
|
+
user0.publicKey,
|
|
1825
|
+
OrderType.TRIGGER_LIMIT,
|
|
1826
|
+
MarketType.PERP,
|
|
1827
|
+
1, //orderId
|
|
1828
|
+
marketIndex, // marketIndex
|
|
1829
|
+
vBid, // price
|
|
1830
|
+
BASE_PRECISION, // baseAssetAmount: BN,
|
|
1831
|
+
PositionDirection.LONG,
|
|
1832
|
+
oracle.price.sub(new BN(1)), // triggerPrice: BN,
|
|
1833
|
+
OrderTriggerCondition.ABOVE, // triggerCondition: OrderTriggerCondition,
|
|
1834
|
+
vBid,
|
|
1835
|
+
vAsk
|
|
1836
|
+
);
|
|
1837
|
+
// should trigger limit sell with above condition
|
|
1838
|
+
insertTriggerOrderToDLOB(
|
|
1839
|
+
dlob,
|
|
1840
|
+
user1.publicKey,
|
|
1841
|
+
OrderType.TRIGGER_LIMIT,
|
|
1842
|
+
MarketType.PERP,
|
|
1843
|
+
2, //orderId
|
|
1844
|
+
marketIndex, // marketIndex
|
|
1845
|
+
vBid, // price
|
|
1846
|
+
BASE_PRECISION, // baseAssetAmount: BN,
|
|
1847
|
+
PositionDirection.SHORT,
|
|
1848
|
+
oracle.price.sub(new BN(1)), // triggerPrice: BN,
|
|
1849
|
+
OrderTriggerCondition.ABOVE, // triggerCondition: OrderTriggerCondition,
|
|
1850
|
+
vBid,
|
|
1851
|
+
vAsk
|
|
1852
|
+
);
|
|
1853
|
+
// should trigger market buy with above condition
|
|
1854
|
+
insertTriggerOrderToDLOB(
|
|
1855
|
+
dlob,
|
|
1856
|
+
user2.publicKey,
|
|
1857
|
+
OrderType.TRIGGER_MARKET,
|
|
1858
|
+
MarketType.PERP,
|
|
1859
|
+
3, //orderId
|
|
1860
|
+
marketIndex, // marketIndex
|
|
1861
|
+
vAsk, // price
|
|
1862
|
+
BASE_PRECISION, // baseAssetAmount: BN,
|
|
1863
|
+
PositionDirection.LONG,
|
|
1864
|
+
oracle.price.sub(new BN(1)), // triggerPrice: BN,
|
|
1865
|
+
OrderTriggerCondition.ABOVE, // triggerCondition: OrderTriggerCondition,
|
|
1866
|
+
vBid,
|
|
1867
|
+
vAsk
|
|
1868
|
+
);
|
|
1869
|
+
// should trigger market sell with above condition
|
|
1870
|
+
insertTriggerOrderToDLOB(
|
|
1871
|
+
dlob,
|
|
1872
|
+
user3.publicKey,
|
|
1873
|
+
OrderType.TRIGGER_MARKET,
|
|
1874
|
+
MarketType.PERP,
|
|
1875
|
+
4, //orderId
|
|
1876
|
+
marketIndex, // marketIndex
|
|
1877
|
+
vBid, // price
|
|
1878
|
+
BASE_PRECISION, // baseAssetAmount: BN,
|
|
1879
|
+
PositionDirection.SHORT,
|
|
1880
|
+
oracle.price.sub(new BN(1)), // triggerPrice: BN,
|
|
1881
|
+
OrderTriggerCondition.ABOVE, // triggerCondition: OrderTriggerCondition,
|
|
1882
|
+
vBid,
|
|
1883
|
+
vAsk
|
|
1884
|
+
);
|
|
1885
|
+
// should trigger limit buy with below condition
|
|
1886
|
+
insertTriggerOrderToDLOB(
|
|
1887
|
+
dlob,
|
|
1888
|
+
user4.publicKey,
|
|
1889
|
+
OrderType.TRIGGER_LIMIT,
|
|
1890
|
+
MarketType.PERP,
|
|
1891
|
+
5, //orderId
|
|
1892
|
+
marketIndex, // marketIndex
|
|
1893
|
+
vBid, // price
|
|
1894
|
+
BASE_PRECISION, // baseAssetAmount: BN,
|
|
1895
|
+
PositionDirection.LONG,
|
|
1896
|
+
oracle.price.add(new BN(1)), // triggerPrice: BN,
|
|
1897
|
+
OrderTriggerCondition.BELOW, // triggerCondition: OrderTriggerCondition,
|
|
1898
|
+
vBid,
|
|
1899
|
+
vAsk
|
|
1900
|
+
);
|
|
1901
|
+
// should trigger limit sell with below condition
|
|
1902
|
+
insertTriggerOrderToDLOB(
|
|
1903
|
+
dlob,
|
|
1904
|
+
user5.publicKey,
|
|
1905
|
+
OrderType.TRIGGER_LIMIT,
|
|
1906
|
+
MarketType.PERP,
|
|
1907
|
+
6, //orderId
|
|
1908
|
+
marketIndex, // marketIndex
|
|
1909
|
+
vBid, // price
|
|
1910
|
+
BASE_PRECISION, // baseAssetAmount: BN,
|
|
1911
|
+
PositionDirection.SHORT,
|
|
1912
|
+
oracle.price.add(new BN(1)), // triggerPrice: BN,
|
|
1913
|
+
OrderTriggerCondition.BELOW, // triggerCondition: OrderTriggerCondition,
|
|
1914
|
+
vBid,
|
|
1915
|
+
vAsk
|
|
1916
|
+
);
|
|
1917
|
+
// should trigger market buy with below condition
|
|
1918
|
+
insertTriggerOrderToDLOB(
|
|
1919
|
+
dlob,
|
|
1920
|
+
user6.publicKey,
|
|
1921
|
+
OrderType.TRIGGER_MARKET,
|
|
1922
|
+
MarketType.PERP,
|
|
1923
|
+
7, //orderId
|
|
1924
|
+
marketIndex, // marketIndex
|
|
1925
|
+
vBid, // price
|
|
1926
|
+
BASE_PRECISION, // baseAssetAmount: BN,
|
|
1927
|
+
PositionDirection.LONG,
|
|
1928
|
+
oracle.price.add(new BN(1)), // triggerPrice: BN,
|
|
1929
|
+
OrderTriggerCondition.BELOW, // triggerCondition: OrderTriggerCondition,
|
|
1930
|
+
vBid,
|
|
1931
|
+
vAsk
|
|
1932
|
+
);
|
|
1933
|
+
// should trigger market sell with below condition
|
|
1934
|
+
insertTriggerOrderToDLOB(
|
|
1935
|
+
dlob,
|
|
1936
|
+
user7.publicKey,
|
|
1937
|
+
OrderType.TRIGGER_MARKET,
|
|
1938
|
+
MarketType.PERP,
|
|
1939
|
+
8, //orderId
|
|
1940
|
+
marketIndex, // marketIndex
|
|
1941
|
+
vBid, // price
|
|
1942
|
+
BASE_PRECISION, // baseAssetAmount: BN,
|
|
1943
|
+
PositionDirection.LONG,
|
|
1944
|
+
oracle.price.add(new BN(1)), // triggerPrice: BN,
|
|
1945
|
+
OrderTriggerCondition.BELOW, // triggerCondition: OrderTriggerCondition,
|
|
1946
|
+
vBid,
|
|
1947
|
+
vAsk
|
|
1948
|
+
);
|
|
1949
|
+
|
|
1950
|
+
// should NOT trigger market sell with above condition
|
|
1951
|
+
insertTriggerOrderToDLOB(
|
|
1952
|
+
dlob,
|
|
1953
|
+
user8.publicKey,
|
|
1954
|
+
OrderType.TRIGGER_MARKET,
|
|
1955
|
+
MarketType.PERP,
|
|
1956
|
+
9, //orderId
|
|
1957
|
+
marketIndex, // marketIndex
|
|
1958
|
+
vBid, // price
|
|
1959
|
+
BASE_PRECISION, // baseAssetAmount: BN,
|
|
1960
|
+
PositionDirection.SHORT,
|
|
1961
|
+
oracle.price.add(new BN(1)), // triggerPrice: BN,
|
|
1962
|
+
OrderTriggerCondition.ABOVE, // triggerCondition: OrderTriggerCondition,
|
|
1963
|
+
vBid,
|
|
1964
|
+
vAsk
|
|
1965
|
+
);
|
|
1966
|
+
// should NOT trigger market sell with below condition
|
|
1967
|
+
insertTriggerOrderToDLOB(
|
|
1968
|
+
dlob,
|
|
1969
|
+
user9.publicKey,
|
|
1970
|
+
OrderType.TRIGGER_MARKET,
|
|
1971
|
+
MarketType.PERP,
|
|
1972
|
+
10, //orderId
|
|
1973
|
+
marketIndex, // marketIndex
|
|
1974
|
+
vBid, // price
|
|
1975
|
+
BASE_PRECISION, // baseAssetAmount: BN,
|
|
1976
|
+
PositionDirection.SHORT,
|
|
1977
|
+
oracle.price.sub(new BN(1)), // triggerPrice: BN,
|
|
1978
|
+
OrderTriggerCondition.BELOW, // triggerCondition: OrderTriggerCondition,
|
|
1979
|
+
vBid,
|
|
1980
|
+
vAsk
|
|
1981
|
+
);
|
|
1982
|
+
// should NOT trigger market buy with above condition
|
|
1983
|
+
insertTriggerOrderToDLOB(
|
|
1984
|
+
dlob,
|
|
1985
|
+
user10.publicKey,
|
|
1986
|
+
OrderType.TRIGGER_MARKET,
|
|
1987
|
+
MarketType.PERP,
|
|
1988
|
+
11, //orderId
|
|
1989
|
+
marketIndex, // marketIndex
|
|
1990
|
+
vBid, // price
|
|
1991
|
+
BASE_PRECISION, // baseAssetAmount: BN,
|
|
1992
|
+
PositionDirection.LONG,
|
|
1993
|
+
oracle.price.add(new BN(1)), // triggerPrice: BN,
|
|
1994
|
+
OrderTriggerCondition.ABOVE, // triggerCondition: OrderTriggerCondition,
|
|
1995
|
+
vBid,
|
|
1996
|
+
vAsk
|
|
1997
|
+
);
|
|
1998
|
+
// should NOT trigger market buy with below condition
|
|
1999
|
+
insertTriggerOrderToDLOB(
|
|
2000
|
+
dlob,
|
|
2001
|
+
user11.publicKey,
|
|
2002
|
+
OrderType.TRIGGER_MARKET,
|
|
2003
|
+
MarketType.PERP,
|
|
2004
|
+
12, //orderId
|
|
2005
|
+
marketIndex, // marketIndex
|
|
2006
|
+
vBid, // price
|
|
2007
|
+
BASE_PRECISION, // baseAssetAmount: BN,
|
|
2008
|
+
PositionDirection.LONG,
|
|
2009
|
+
oracle.price.sub(new BN(1)), // triggerPrice: BN,
|
|
2010
|
+
OrderTriggerCondition.BELOW, // triggerCondition: OrderTriggerCondition,
|
|
2011
|
+
vBid,
|
|
2012
|
+
vAsk
|
|
2013
|
+
);
|
|
2014
|
+
|
|
2015
|
+
const nodesToTrigger = dlob.findNodesToTrigger(
|
|
2016
|
+
marketIndex,
|
|
2017
|
+
slot,
|
|
2018
|
+
oracle.price,
|
|
2019
|
+
MarketType.PERP
|
|
2020
|
+
);
|
|
2021
|
+
console.log(`nodesToTriggeR: ${nodesToTrigger.length}`);
|
|
2022
|
+
for (const [idx, n] of nodesToTrigger.entries()) {
|
|
2023
|
+
expect(n.node.order?.orderId).to.equal(orderIdsToTrigger[idx]);
|
|
2024
|
+
console.log(`nodeToTrigger: ${n.node.order?.orderId.toString()}`);
|
|
2025
|
+
}
|
|
2026
|
+
});
|
|
2027
|
+
|
|
2028
|
+
it('Test will return expired market orders to fill', () => {
|
|
2029
|
+
const vAsk = new BN(15);
|
|
2030
|
+
const vBid = new BN(8);
|
|
2031
|
+
|
|
2032
|
+
const mockUserMap = new MockUserMap();
|
|
2033
|
+
const user0 = Keypair.generate();
|
|
2034
|
+
const user0Auth = Keypair.generate();
|
|
2035
|
+
const user1 = Keypair.generate();
|
|
2036
|
+
const user1Auth = Keypair.generate();
|
|
2037
|
+
mockUserMap.addUserAccountAuthority(user0.publicKey, user0Auth.publicKey);
|
|
2038
|
+
mockUserMap.addUserAccountAuthority(user1.publicKey, user1Auth.publicKey);
|
|
2039
|
+
|
|
2040
|
+
const dlob = new DLOB(
|
|
2041
|
+
mockPerpMarkets,
|
|
2042
|
+
mockSpotMarkets,
|
|
2043
|
+
mockStateAccount,
|
|
2044
|
+
mockUserMap,
|
|
2045
|
+
false
|
|
2046
|
+
);
|
|
2047
|
+
const marketIndex = 0;
|
|
2048
|
+
|
|
2049
|
+
const slot = 20;
|
|
2050
|
+
const ts = 20;
|
|
2051
|
+
const maxTs = new BN(30);
|
|
2052
|
+
|
|
2053
|
+
// non crossing bid
|
|
2054
|
+
insertOrderToDLOB(
|
|
2055
|
+
dlob,
|
|
2056
|
+
user0.publicKey,
|
|
2057
|
+
OrderType.MARKET,
|
|
2058
|
+
MarketType.PERP,
|
|
2059
|
+
255, // orderId
|
|
2060
|
+
marketIndex,
|
|
2061
|
+
new BN(2), // price, very low, don't cross vamm
|
|
2062
|
+
BASE_PRECISION, // quantity
|
|
2063
|
+
PositionDirection.LONG,
|
|
2064
|
+
vBid,
|
|
2065
|
+
vAsk,
|
|
2066
|
+
new BN(slot),
|
|
2067
|
+
maxTs
|
|
2068
|
+
);
|
|
2069
|
+
insertOrderToDLOB(
|
|
2070
|
+
dlob,
|
|
2071
|
+
user1.publicKey,
|
|
2072
|
+
OrderType.MARKET,
|
|
2073
|
+
MarketType.PERP,
|
|
2074
|
+
2, // orderId
|
|
2075
|
+
marketIndex,
|
|
2076
|
+
new BN(30), // price, very high, don't cross vamm
|
|
2077
|
+
BASE_PRECISION, // quantity
|
|
2078
|
+
PositionDirection.SHORT,
|
|
2079
|
+
vAsk,
|
|
2080
|
+
vBid,
|
|
2081
|
+
new BN(slot),
|
|
2082
|
+
maxTs
|
|
2083
|
+
);
|
|
2084
|
+
|
|
2085
|
+
// order auction is not yet complete, and order is not expired.
|
|
2086
|
+
const slot0 = slot;
|
|
2087
|
+
const ts0 = ts;
|
|
2088
|
+
const nodesToFillBefore = dlob.findNodesToFill(
|
|
2089
|
+
marketIndex,
|
|
2090
|
+
vBid,
|
|
2091
|
+
vAsk,
|
|
2092
|
+
slot0,
|
|
2093
|
+
ts0,
|
|
2094
|
+
MarketType.PERP,
|
|
2095
|
+
{
|
|
2096
|
+
price: vBid.add(vAsk).div(new BN(2)),
|
|
2097
|
+
slot: new BN(slot0),
|
|
2098
|
+
confidence: new BN(1),
|
|
2099
|
+
hasSufficientNumberOfDataPoints: true,
|
|
2100
|
+
}
|
|
2101
|
+
);
|
|
2102
|
+
expect(nodesToFillBefore.length).to.equal(0);
|
|
2103
|
+
|
|
2104
|
+
// should get order to fill after timeInForce
|
|
2105
|
+
const slot1 = slot0 + 20;
|
|
2106
|
+
const ts1 = ts0 + 20; // overshoots expiry
|
|
2107
|
+
const nodesToFillAfter = dlob.findNodesToFill(
|
|
2108
|
+
marketIndex,
|
|
2109
|
+
vBid,
|
|
2110
|
+
vAsk,
|
|
2111
|
+
slot1, // auction is over, and order ix expired
|
|
2112
|
+
ts1,
|
|
2113
|
+
MarketType.PERP,
|
|
2114
|
+
{
|
|
2115
|
+
price: vBid.add(vAsk).div(new BN(2)),
|
|
2116
|
+
slot: new BN(slot1),
|
|
2117
|
+
confidence: new BN(1),
|
|
2118
|
+
hasSufficientNumberOfDataPoints: true,
|
|
2119
|
+
}
|
|
2120
|
+
);
|
|
2121
|
+
console.log(`Filled nodes: ${nodesToFillAfter.length}`);
|
|
2122
|
+
for (const n of nodesToFillAfter) {
|
|
2123
|
+
printCrossedNodes(n, slot1);
|
|
2124
|
+
}
|
|
2125
|
+
expect(nodesToFillAfter.length).to.equal(2);
|
|
2126
|
+
|
|
2127
|
+
// check that the nodes have no makers
|
|
2128
|
+
expect(nodesToFillAfter[0].makerNode).to.equal(undefined);
|
|
2129
|
+
expect(nodesToFillAfter[1].makerNode).to.equal(undefined);
|
|
2130
|
+
});
|
|
2131
|
+
|
|
2132
|
+
it('Test skips vAMM and fills market buy order with floating limit order during auction', () => {
|
|
2133
|
+
const vAsk = new BN(15).mul(PRICE_PRECISION);
|
|
2134
|
+
const vBid = new BN(8).mul(PRICE_PRECISION);
|
|
2135
|
+
|
|
2136
|
+
const mockUserMap = new MockUserMap();
|
|
2137
|
+
const user0 = Keypair.generate();
|
|
2138
|
+
const user0Auth = Keypair.generate();
|
|
2139
|
+
const user1 = Keypair.generate();
|
|
2140
|
+
const user1Auth = Keypair.generate();
|
|
2141
|
+
const user2 = Keypair.generate();
|
|
2142
|
+
const user2Auth = Keypair.generate();
|
|
2143
|
+
const user3 = Keypair.generate();
|
|
2144
|
+
const user3Auth = Keypair.generate();
|
|
2145
|
+
const user4 = Keypair.generate();
|
|
2146
|
+
const user4Auth = Keypair.generate();
|
|
2147
|
+
mockUserMap.addUserAccountAuthority(user0.publicKey, user0Auth.publicKey);
|
|
2148
|
+
mockUserMap.addUserAccountAuthority(user1.publicKey, user1Auth.publicKey);
|
|
2149
|
+
mockUserMap.addUserAccountAuthority(user2.publicKey, user2Auth.publicKey);
|
|
2150
|
+
mockUserMap.addUserAccountAuthority(user3.publicKey, user3Auth.publicKey);
|
|
2151
|
+
mockUserMap.addUserAccountAuthority(user4.publicKey, user4Auth.publicKey);
|
|
2152
|
+
|
|
2153
|
+
const dlob = new DLOB(
|
|
2154
|
+
mockPerpMarkets,
|
|
2155
|
+
mockSpotMarkets,
|
|
2156
|
+
mockStateAccount,
|
|
2157
|
+
mockUserMap,
|
|
2158
|
+
false
|
|
2159
|
+
);
|
|
2160
|
+
const marketIndex = 0;
|
|
2161
|
+
|
|
2162
|
+
const slot = 12;
|
|
2163
|
+
const ts = 12;
|
|
2164
|
+
const oracle = {
|
|
2165
|
+
price: vBid.add(vAsk).div(new BN(2)),
|
|
2166
|
+
slot: new BN(slot),
|
|
2167
|
+
confidence: new BN(1),
|
|
2168
|
+
hasSufficientNumberOfDataPoints: true,
|
|
2169
|
+
};
|
|
2170
|
+
|
|
2171
|
+
// insert some floating limit sells above vAMM ask
|
|
2172
|
+
insertOrderToDLOB(
|
|
2173
|
+
dlob,
|
|
2174
|
+
user0.publicKey,
|
|
2175
|
+
OrderType.LIMIT,
|
|
2176
|
+
MarketType.PERP,
|
|
2177
|
+
1, // orderId
|
|
2178
|
+
marketIndex,
|
|
2179
|
+
new BN(14).mul(PRICE_PRECISION), // price
|
|
2180
|
+
BASE_PRECISION, // quantity
|
|
2181
|
+
PositionDirection.SHORT,
|
|
2182
|
+
vBid,
|
|
2183
|
+
vAsk,
|
|
2184
|
+
new BN(slot),
|
|
2185
|
+
ZERO,
|
|
2186
|
+
new BN(1).mul(PRICE_PRECISION)
|
|
2187
|
+
);
|
|
2188
|
+
insertOrderToDLOB(
|
|
2189
|
+
dlob,
|
|
2190
|
+
user1.publicKey,
|
|
2191
|
+
OrderType.LIMIT,
|
|
2192
|
+
MarketType.PERP,
|
|
2193
|
+
2, // orderId
|
|
2194
|
+
marketIndex,
|
|
2195
|
+
new BN(13).mul(PRICE_PRECISION), // price
|
|
2196
|
+
BASE_PRECISION, // quantity
|
|
2197
|
+
PositionDirection.SHORT,
|
|
2198
|
+
vBid,
|
|
2199
|
+
vAsk,
|
|
2200
|
+
new BN(slot),
|
|
2201
|
+
ZERO,
|
|
2202
|
+
new BN(1).mul(PRICE_PRECISION)
|
|
2203
|
+
);
|
|
2204
|
+
insertOrderToDLOB(
|
|
2205
|
+
dlob,
|
|
2206
|
+
user2.publicKey,
|
|
2207
|
+
OrderType.LIMIT,
|
|
2208
|
+
MarketType.PERP,
|
|
2209
|
+
3, // orderId
|
|
2210
|
+
marketIndex,
|
|
2211
|
+
new BN(9).mul(PRICE_PRECISION), // price <-- best price
|
|
2212
|
+
new BN(3).mul(BASE_PRECISION), // quantity
|
|
2213
|
+
PositionDirection.SHORT,
|
|
2214
|
+
vBid,
|
|
2215
|
+
vAsk,
|
|
2216
|
+
new BN(slot),
|
|
2217
|
+
ZERO,
|
|
2218
|
+
new BN(1).mul(PRICE_PRECISION)
|
|
2219
|
+
);
|
|
2220
|
+
|
|
2221
|
+
// should have no crossing orders
|
|
2222
|
+
const nodesToFillBefore = dlob.findNodesToFill(
|
|
2223
|
+
marketIndex,
|
|
2224
|
+
vBid,
|
|
2225
|
+
vAsk,
|
|
2226
|
+
slot,
|
|
2227
|
+
ts,
|
|
2228
|
+
MarketType.PERP,
|
|
2229
|
+
{
|
|
2230
|
+
price: vBid.add(vAsk).div(new BN(2)),
|
|
2231
|
+
slot: new BN(slot),
|
|
2232
|
+
confidence: new BN(1),
|
|
2233
|
+
hasSufficientNumberOfDataPoints: true,
|
|
2234
|
+
}
|
|
2235
|
+
);
|
|
2236
|
+
expect(nodesToFillBefore.length).to.equal(0);
|
|
2237
|
+
|
|
2238
|
+
// place two market buy orders to eat the best ask
|
|
2239
|
+
insertOrderToDLOB(
|
|
2240
|
+
dlob,
|
|
2241
|
+
user3.publicKey,
|
|
2242
|
+
OrderType.MARKET,
|
|
2243
|
+
MarketType.PERP,
|
|
2244
|
+
4, // orderId
|
|
2245
|
+
marketIndex,
|
|
2246
|
+
new BN(15).mul(PRICE_PRECISION), // price
|
|
2247
|
+
new BN(1).mul(BASE_PRECISION), // quantity
|
|
2248
|
+
PositionDirection.LONG,
|
|
2249
|
+
vBid,
|
|
2250
|
+
vAsk
|
|
2251
|
+
);
|
|
2252
|
+
insertOrderToDLOB(
|
|
2253
|
+
dlob,
|
|
2254
|
+
user4.publicKey,
|
|
2255
|
+
OrderType.MARKET,
|
|
2256
|
+
MarketType.PERP,
|
|
2257
|
+
5, // orderId
|
|
2258
|
+
marketIndex,
|
|
2259
|
+
new BN(15).mul(PRICE_PRECISION), // price
|
|
2260
|
+
new BN(2).mul(BASE_PRECISION), // quantity
|
|
2261
|
+
PositionDirection.LONG,
|
|
2262
|
+
vBid,
|
|
2263
|
+
vAsk
|
|
2264
|
+
);
|
|
2265
|
+
|
|
2266
|
+
const nodesToFillAfter = dlob.findNodesToFill(
|
|
2267
|
+
marketIndex,
|
|
2268
|
+
vBid,
|
|
2269
|
+
vAsk,
|
|
2270
|
+
slot,
|
|
2271
|
+
ts,
|
|
2272
|
+
MarketType.PERP,
|
|
2273
|
+
{
|
|
2274
|
+
price: vBid.add(vAsk).div(new BN(2)),
|
|
2275
|
+
slot: new BN(slot),
|
|
2276
|
+
confidence: new BN(1),
|
|
2277
|
+
hasSufficientNumberOfDataPoints: true,
|
|
2278
|
+
}
|
|
2279
|
+
);
|
|
2280
|
+
const mktNodes = dlob.findExpiredNodesToFill(
|
|
2281
|
+
marketIndex,
|
|
2282
|
+
slot,
|
|
2283
|
+
MarketType.PERP
|
|
2284
|
+
);
|
|
2285
|
+
console.log(`market nodes: ${mktNodes.length}`);
|
|
2286
|
+
|
|
2287
|
+
printBookState(dlob, marketIndex, vBid, vAsk, slot, oracle);
|
|
2288
|
+
|
|
2289
|
+
console.log(`Filled nodes: ${nodesToFillAfter.length}`);
|
|
2290
|
+
for (const n of nodesToFillAfter) {
|
|
2291
|
+
printCrossedNodes(n, slot);
|
|
2292
|
+
}
|
|
2293
|
+
expect(nodesToFillAfter.length).to.equal(3);
|
|
2294
|
+
|
|
2295
|
+
// taker should fill first order completely with best maker (1/1)
|
|
2296
|
+
expect(nodesToFillAfter[0].node.order?.orderId).to.equal(4);
|
|
2297
|
+
expect(nodesToFillAfter[0].makerNode?.order?.orderId).to.equal(1);
|
|
2298
|
+
|
|
2299
|
+
// taker should fill partially with second best maker (1/2)
|
|
2300
|
+
expect(nodesToFillAfter[1].node.order?.orderId).to.equal(5);
|
|
2301
|
+
expect(nodesToFillAfter[1].makerNode?.order?.orderId).to.equal(2);
|
|
2302
|
+
|
|
2303
|
+
// taker should fill completely with third best maker (2/2)
|
|
2304
|
+
expect(nodesToFillAfter[2].node.order?.orderId).to.equal(5);
|
|
2305
|
+
expect(nodesToFillAfter[2].makerNode?.order?.orderId).to.equal(3);
|
|
2306
|
+
});
|
|
2307
|
+
|
|
2308
|
+
it('Test fills market buy order with better priced vAMM after auction', () => {
|
|
2309
|
+
const vAsk = new BN(15).mul(PRICE_PRECISION);
|
|
2310
|
+
const vBid = new BN(8).mul(PRICE_PRECISION);
|
|
2311
|
+
|
|
2312
|
+
const mockUserMap = new MockUserMap();
|
|
2313
|
+
const user0 = Keypair.generate();
|
|
2314
|
+
const user0Auth = Keypair.generate();
|
|
2315
|
+
const user1 = Keypair.generate();
|
|
2316
|
+
const user1Auth = Keypair.generate();
|
|
2317
|
+
const user2 = Keypair.generate();
|
|
2318
|
+
const user2Auth = Keypair.generate();
|
|
2319
|
+
const user3 = Keypair.generate();
|
|
2320
|
+
const user3Auth = Keypair.generate();
|
|
2321
|
+
const user4 = Keypair.generate();
|
|
2322
|
+
const user4Auth = Keypair.generate();
|
|
2323
|
+
mockUserMap.addUserAccountAuthority(user0.publicKey, user0Auth.publicKey);
|
|
2324
|
+
mockUserMap.addUserAccountAuthority(user1.publicKey, user1Auth.publicKey);
|
|
2325
|
+
mockUserMap.addUserAccountAuthority(user2.publicKey, user2Auth.publicKey);
|
|
2326
|
+
mockUserMap.addUserAccountAuthority(user3.publicKey, user3Auth.publicKey);
|
|
2327
|
+
mockUserMap.addUserAccountAuthority(user4.publicKey, user4Auth.publicKey);
|
|
2328
|
+
|
|
2329
|
+
const dlob = new DLOB(
|
|
2330
|
+
mockPerpMarkets,
|
|
2331
|
+
mockSpotMarkets,
|
|
2332
|
+
mockStateAccount,
|
|
2333
|
+
mockUserMap,
|
|
2334
|
+
false
|
|
2335
|
+
);
|
|
2336
|
+
const marketIndex = 0;
|
|
2337
|
+
|
|
2338
|
+
const slot = 12;
|
|
2339
|
+
const ts = 12;
|
|
2340
|
+
const oracle = {
|
|
2341
|
+
price: vBid.add(vAsk).div(new BN(2)),
|
|
2342
|
+
slot: new BN(slot),
|
|
2343
|
+
confidence: new BN(1),
|
|
2344
|
+
hasSufficientNumberOfDataPoints: true,
|
|
2345
|
+
};
|
|
2346
|
+
|
|
2347
|
+
// insert some floating limit sells above vAMM ask
|
|
2348
|
+
insertOrderToDLOB(
|
|
2349
|
+
dlob,
|
|
2350
|
+
user0.publicKey,
|
|
2351
|
+
OrderType.LIMIT,
|
|
2352
|
+
MarketType.PERP,
|
|
2353
|
+
1, // orderId
|
|
2354
|
+
marketIndex,
|
|
2355
|
+
new BN(14).mul(PRICE_PRECISION), // price
|
|
2356
|
+
BASE_PRECISION, // quantity
|
|
2357
|
+
PositionDirection.SHORT,
|
|
2358
|
+
vBid,
|
|
2359
|
+
vAsk,
|
|
2360
|
+
new BN(slot),
|
|
2361
|
+
ZERO,
|
|
2362
|
+
new BN(3).mul(PRICE_PRECISION)
|
|
2363
|
+
);
|
|
2364
|
+
insertOrderToDLOB(
|
|
2365
|
+
dlob,
|
|
2366
|
+
user1.publicKey,
|
|
2367
|
+
OrderType.LIMIT,
|
|
2368
|
+
MarketType.PERP,
|
|
2369
|
+
2, // orderId
|
|
2370
|
+
marketIndex,
|
|
2371
|
+
new BN(13).mul(PRICE_PRECISION), // price
|
|
2372
|
+
BASE_PRECISION, // quantity
|
|
2373
|
+
PositionDirection.SHORT,
|
|
2374
|
+
vBid,
|
|
2375
|
+
vAsk,
|
|
2376
|
+
new BN(slot),
|
|
2377
|
+
ZERO,
|
|
2378
|
+
new BN(4).mul(PRICE_PRECISION)
|
|
2379
|
+
);
|
|
2380
|
+
insertOrderToDLOB(
|
|
2381
|
+
dlob,
|
|
2382
|
+
user2.publicKey,
|
|
2383
|
+
OrderType.LIMIT,
|
|
2384
|
+
MarketType.PERP,
|
|
2385
|
+
3, // orderId
|
|
2386
|
+
marketIndex,
|
|
2387
|
+
new BN(9).mul(PRICE_PRECISION), // price <-- best price
|
|
2388
|
+
new BN(3).mul(BASE_PRECISION), // quantity
|
|
2389
|
+
PositionDirection.SHORT,
|
|
2390
|
+
vBid,
|
|
2391
|
+
vAsk,
|
|
2392
|
+
new BN(slot),
|
|
2393
|
+
ZERO,
|
|
2394
|
+
new BN(5).mul(PRICE_PRECISION)
|
|
2395
|
+
);
|
|
2396
|
+
|
|
2397
|
+
// should have no crossing orders
|
|
2398
|
+
const auctionOverSlot = slot * 10;
|
|
2399
|
+
const auctionOverTs = ts * 10;
|
|
2400
|
+
const nodesToFillBefore = dlob.findCrossingNodesToFill(
|
|
2401
|
+
marketIndex,
|
|
2402
|
+
auctionOverSlot, // auction over
|
|
2403
|
+
MarketType.PERP,
|
|
2404
|
+
oracle
|
|
2405
|
+
);
|
|
2406
|
+
expect(nodesToFillBefore.length).to.equal(0);
|
|
2407
|
+
|
|
2408
|
+
// place two market buy orders
|
|
2409
|
+
insertOrderToDLOB(
|
|
2410
|
+
dlob,
|
|
2411
|
+
user3.publicKey,
|
|
2412
|
+
OrderType.MARKET,
|
|
2413
|
+
MarketType.PERP,
|
|
2414
|
+
4, // orderId
|
|
2415
|
+
marketIndex,
|
|
2416
|
+
new BN(15).mul(PRICE_PRECISION), // price
|
|
2417
|
+
new BN(1).mul(BASE_PRECISION), // quantity
|
|
2418
|
+
PositionDirection.LONG,
|
|
2419
|
+
vBid,
|
|
2420
|
+
vAsk,
|
|
2421
|
+
new BN(slot),
|
|
2422
|
+
ZERO
|
|
2423
|
+
);
|
|
2424
|
+
insertOrderToDLOB(
|
|
2425
|
+
dlob,
|
|
2426
|
+
user4.publicKey,
|
|
2427
|
+
OrderType.MARKET,
|
|
2428
|
+
MarketType.PERP,
|
|
2429
|
+
5, // orderId
|
|
2430
|
+
marketIndex,
|
|
2431
|
+
new BN(15).mul(PRICE_PRECISION), // price
|
|
2432
|
+
new BN(2).mul(BASE_PRECISION), // quantity
|
|
2433
|
+
PositionDirection.LONG,
|
|
2434
|
+
vBid,
|
|
2435
|
+
vAsk,
|
|
2436
|
+
new BN(slot),
|
|
2437
|
+
ZERO
|
|
2438
|
+
);
|
|
2439
|
+
|
|
2440
|
+
const nodesToFillAfter = dlob.findNodesToFill(
|
|
2441
|
+
marketIndex,
|
|
2442
|
+
vBid,
|
|
2443
|
+
vAsk,
|
|
2444
|
+
auctionOverTs, // auction in progress
|
|
2445
|
+
ts,
|
|
2446
|
+
MarketType.PERP,
|
|
2447
|
+
oracle
|
|
2448
|
+
);
|
|
2449
|
+
|
|
2450
|
+
console.log(`Filled nodes: ${nodesToFillAfter.length}`);
|
|
2451
|
+
for (const n of nodesToFillAfter) {
|
|
2452
|
+
printCrossedNodes(n, auctionOverSlot);
|
|
2453
|
+
}
|
|
2454
|
+
expect(nodesToFillAfter.length).to.equal(2);
|
|
2455
|
+
|
|
2456
|
+
// taker should fill completely with best maker
|
|
2457
|
+
expect(nodesToFillAfter[0].node.order?.orderId).to.equal(4);
|
|
2458
|
+
expect(nodesToFillAfter[0].makerNode?.order?.orderId).to.equal(1);
|
|
2459
|
+
|
|
2460
|
+
// taker should fill the rest with the vAMM
|
|
2461
|
+
expect(nodesToFillAfter[1].node.order?.orderId).to.equal(5);
|
|
2462
|
+
expect(nodesToFillAfter[1].makerNode?.order?.orderId).to.equal(undefined);
|
|
2463
|
+
});
|
|
2464
|
+
|
|
2465
|
+
it('Test skips vAMM and fills market sell order with floating limit buys during auction', () => {
|
|
2466
|
+
const vAsk = new BN(15).mul(PRICE_PRECISION);
|
|
2467
|
+
const vBid = new BN(8).mul(PRICE_PRECISION);
|
|
2468
|
+
|
|
2469
|
+
const mockUserMap = new MockUserMap();
|
|
2470
|
+
const user0 = Keypair.generate();
|
|
2471
|
+
const user0Auth = Keypair.generate();
|
|
2472
|
+
const user1 = Keypair.generate();
|
|
2473
|
+
const user1Auth = Keypair.generate();
|
|
2474
|
+
const user2 = Keypair.generate();
|
|
2475
|
+
const user2Auth = Keypair.generate();
|
|
2476
|
+
const user3 = Keypair.generate();
|
|
2477
|
+
const user3Auth = Keypair.generate();
|
|
2478
|
+
const user4 = Keypair.generate();
|
|
2479
|
+
const user4Auth = Keypair.generate();
|
|
2480
|
+
mockUserMap.addUserAccountAuthority(user0.publicKey, user0Auth.publicKey);
|
|
2481
|
+
mockUserMap.addUserAccountAuthority(user1.publicKey, user1Auth.publicKey);
|
|
2482
|
+
mockUserMap.addUserAccountAuthority(user2.publicKey, user2Auth.publicKey);
|
|
2483
|
+
mockUserMap.addUserAccountAuthority(user3.publicKey, user3Auth.publicKey);
|
|
2484
|
+
mockUserMap.addUserAccountAuthority(user4.publicKey, user4Auth.publicKey);
|
|
2485
|
+
|
|
2486
|
+
const dlob = new DLOB(
|
|
2487
|
+
mockPerpMarkets,
|
|
2488
|
+
mockSpotMarkets,
|
|
2489
|
+
mockStateAccount,
|
|
2490
|
+
mockUserMap,
|
|
2491
|
+
false
|
|
2492
|
+
);
|
|
2493
|
+
const marketIndex = 0;
|
|
2494
|
+
|
|
2495
|
+
const slot = 12;
|
|
2496
|
+
const oracle = {
|
|
2497
|
+
price: vBid.add(vAsk).div(new BN(2)), // 11.5
|
|
2498
|
+
slot: new BN(slot),
|
|
2499
|
+
confidence: new BN(1),
|
|
2500
|
+
hasSufficientNumberOfDataPoints: true,
|
|
2501
|
+
};
|
|
2502
|
+
|
|
2503
|
+
// insert some floating limit buy below vAMM bid
|
|
2504
|
+
insertOrderToDLOB(
|
|
2505
|
+
dlob,
|
|
2506
|
+
user0.publicKey,
|
|
2507
|
+
OrderType.LIMIT,
|
|
2508
|
+
MarketType.PERP,
|
|
2509
|
+
2, // orderId
|
|
2510
|
+
marketIndex,
|
|
2511
|
+
new BN(0).mul(PRICE_PRECISION), // price, ignored since it's a floating limit
|
|
2512
|
+
BASE_PRECISION, // quantity
|
|
2513
|
+
PositionDirection.LONG,
|
|
2514
|
+
vBid,
|
|
2515
|
+
vAsk,
|
|
2516
|
+
new BN(slot),
|
|
2517
|
+
ZERO,
|
|
2518
|
+
new BN(-4).mul(PRICE_PRECISION) // second best bid, but worse than vBid (8): 7.5
|
|
2519
|
+
);
|
|
2520
|
+
insertOrderToDLOB(
|
|
2521
|
+
dlob,
|
|
2522
|
+
user1.publicKey,
|
|
2523
|
+
OrderType.LIMIT,
|
|
2524
|
+
MarketType.PERP,
|
|
2525
|
+
3, // orderId
|
|
2526
|
+
marketIndex,
|
|
2527
|
+
new BN(0).mul(PRICE_PRECISION), // price; ignored since it's a floating limit
|
|
2528
|
+
new BN(1).mul(BASE_PRECISION), // quantity
|
|
2529
|
+
PositionDirection.LONG,
|
|
2530
|
+
vBid,
|
|
2531
|
+
vAsk,
|
|
2532
|
+
new BN(slot),
|
|
2533
|
+
ZERO,
|
|
2534
|
+
new BN(-1).mul(PRICE_PRECISION) // best price: 10.5
|
|
2535
|
+
);
|
|
2536
|
+
insertOrderToDLOB(
|
|
2537
|
+
dlob,
|
|
2538
|
+
user2.publicKey,
|
|
2539
|
+
OrderType.LIMIT,
|
|
2540
|
+
MarketType.PERP,
|
|
2541
|
+
1, // orderId
|
|
2542
|
+
marketIndex,
|
|
2543
|
+
new BN(0).mul(PRICE_PRECISION), // price; ignored since it's a floating limit
|
|
2544
|
+
BASE_PRECISION, // quantity
|
|
2545
|
+
PositionDirection.LONG,
|
|
2546
|
+
vBid,
|
|
2547
|
+
vAsk,
|
|
2548
|
+
new BN(slot),
|
|
2549
|
+
ZERO,
|
|
2550
|
+
new BN(-5).mul(PRICE_PRECISION) // third best bid, worse than vBid (8): 6.5
|
|
2551
|
+
);
|
|
2552
|
+
|
|
2553
|
+
// should have no crossing orders
|
|
2554
|
+
const nodesToFillBefore = dlob.findCrossingNodesToFill(
|
|
2555
|
+
marketIndex,
|
|
2556
|
+
slot,
|
|
2557
|
+
MarketType.PERP,
|
|
2558
|
+
oracle
|
|
2559
|
+
);
|
|
2560
|
+
expect(nodesToFillBefore.length).to.equal(0);
|
|
2561
|
+
|
|
2562
|
+
// place two market sell orders to eat the best bid
|
|
2563
|
+
insertOrderToDLOB(
|
|
2564
|
+
dlob,
|
|
2565
|
+
user3.publicKey,
|
|
2566
|
+
OrderType.MARKET,
|
|
2567
|
+
MarketType.PERP,
|
|
2568
|
+
4, // orderId
|
|
2569
|
+
marketIndex,
|
|
2570
|
+
new BN(5).mul(PRICE_PRECISION), // price
|
|
2571
|
+
new BN(1).mul(BASE_PRECISION), // quantity
|
|
2572
|
+
PositionDirection.SHORT,
|
|
2573
|
+
vBid,
|
|
2574
|
+
vAsk
|
|
2575
|
+
);
|
|
2576
|
+
insertOrderToDLOB(
|
|
2577
|
+
dlob,
|
|
2578
|
+
user4.publicKey,
|
|
2579
|
+
OrderType.MARKET,
|
|
2580
|
+
MarketType.PERP,
|
|
2581
|
+
5, // orderId
|
|
2582
|
+
marketIndex,
|
|
2583
|
+
new BN(4).mul(PRICE_PRECISION), // price
|
|
2584
|
+
new BN(2).mul(BASE_PRECISION), // quantity
|
|
2585
|
+
PositionDirection.SHORT,
|
|
2586
|
+
vBid,
|
|
2587
|
+
vAsk
|
|
2588
|
+
);
|
|
2589
|
+
|
|
2590
|
+
const nodesToFillAfter = dlob.findCrossingNodesToFill(
|
|
2591
|
+
marketIndex,
|
|
2592
|
+
slot, // auction in progress
|
|
2593
|
+
MarketType.PERP,
|
|
2594
|
+
oracle
|
|
2595
|
+
);
|
|
2596
|
+
const mktNodes = dlob.findExpiredNodesToFill(
|
|
2597
|
+
marketIndex,
|
|
2598
|
+
slot,
|
|
2599
|
+
MarketType.PERP
|
|
2600
|
+
);
|
|
2601
|
+
console.log(`market nodes: ${mktNodes.length}`);
|
|
2602
|
+
|
|
2603
|
+
printBookState(dlob, marketIndex, vBid, vAsk, slot, oracle);
|
|
2604
|
+
|
|
2605
|
+
console.log(`Filled nodes: ${nodesToFillAfter.length}`);
|
|
2606
|
+
for (const n of nodesToFillAfter) {
|
|
2607
|
+
printCrossedNodes(n, slot);
|
|
2608
|
+
}
|
|
2609
|
+
expect(nodesToFillAfter.length).to.equal(3);
|
|
2610
|
+
|
|
2611
|
+
// taker should fill first order completely with best maker (1/1)
|
|
2612
|
+
expect(
|
|
2613
|
+
nodesToFillAfter[0].node.order?.orderId,
|
|
2614
|
+
'wrong taker orderId'
|
|
2615
|
+
).to.equal(4);
|
|
2616
|
+
expect(
|
|
2617
|
+
nodesToFillAfter[0].makerNode?.order?.orderId,
|
|
2618
|
+
'wrong maker orderId'
|
|
2619
|
+
).to.equal(3);
|
|
2620
|
+
|
|
2621
|
+
// taker should fill partially with second best maker (1/2)
|
|
2622
|
+
expect(
|
|
2623
|
+
nodesToFillAfter[1].node.order?.orderId,
|
|
2624
|
+
'wrong maker orderId'
|
|
2625
|
+
).to.equal(5);
|
|
2626
|
+
expect(
|
|
2627
|
+
nodesToFillAfter[1].makerNode?.order?.orderId,
|
|
2628
|
+
'wrong maker orderId'
|
|
2629
|
+
).to.equal(2);
|
|
2630
|
+
|
|
2631
|
+
// taker should fill completely with third best maker (2/2)
|
|
2632
|
+
expect(
|
|
2633
|
+
nodesToFillAfter[2].node.order?.orderId,
|
|
2634
|
+
'wrong taker orderId'
|
|
2635
|
+
).to.equal(5);
|
|
2636
|
+
expect(
|
|
2637
|
+
nodesToFillAfter[2].makerNode?.order?.orderId,
|
|
2638
|
+
'wrong maker orderId'
|
|
2639
|
+
).to.equal(1);
|
|
2640
|
+
});
|
|
2641
|
+
|
|
2642
|
+
it('Test fills market sell order with better priced vAMM after auction', () => {
|
|
2643
|
+
const vAsk = new BN(15).mul(PRICE_PRECISION);
|
|
2644
|
+
const vBid = new BN(8).mul(PRICE_PRECISION);
|
|
2645
|
+
|
|
2646
|
+
const mockUserMap = new MockUserMap();
|
|
2647
|
+
const user0 = Keypair.generate();
|
|
2648
|
+
const user0Auth = Keypair.generate();
|
|
2649
|
+
const user1 = Keypair.generate();
|
|
2650
|
+
const user1Auth = Keypair.generate();
|
|
2651
|
+
const user2 = Keypair.generate();
|
|
2652
|
+
const user2Auth = Keypair.generate();
|
|
2653
|
+
const user3 = Keypair.generate();
|
|
2654
|
+
const user3Auth = Keypair.generate();
|
|
2655
|
+
const user4 = Keypair.generate();
|
|
2656
|
+
const user4Auth = Keypair.generate();
|
|
2657
|
+
mockUserMap.addUserAccountAuthority(user0.publicKey, user0Auth.publicKey);
|
|
2658
|
+
mockUserMap.addUserAccountAuthority(user1.publicKey, user1Auth.publicKey);
|
|
2659
|
+
mockUserMap.addUserAccountAuthority(user2.publicKey, user2Auth.publicKey);
|
|
2660
|
+
mockUserMap.addUserAccountAuthority(user3.publicKey, user3Auth.publicKey);
|
|
2661
|
+
mockUserMap.addUserAccountAuthority(user4.publicKey, user4Auth.publicKey);
|
|
2662
|
+
|
|
2663
|
+
const dlob = new DLOB(
|
|
2664
|
+
mockPerpMarkets,
|
|
2665
|
+
mockSpotMarkets,
|
|
2666
|
+
mockStateAccount,
|
|
2667
|
+
mockUserMap,
|
|
2668
|
+
false
|
|
2669
|
+
);
|
|
2670
|
+
const marketIndex = 0;
|
|
2671
|
+
|
|
2672
|
+
const slot = 12;
|
|
2673
|
+
const ts = 12;
|
|
2674
|
+
const oracle = {
|
|
2675
|
+
price: vBid.add(vAsk).div(new BN(2)), // 11.5
|
|
2676
|
+
slot: new BN(slot),
|
|
2677
|
+
confidence: new BN(1),
|
|
2678
|
+
hasSufficientNumberOfDataPoints: true,
|
|
2679
|
+
};
|
|
2680
|
+
|
|
2681
|
+
// insert some floating limit buy below vAMM bid
|
|
2682
|
+
insertOrderToDLOB(
|
|
2683
|
+
dlob,
|
|
2684
|
+
user0.publicKey,
|
|
2685
|
+
OrderType.LIMIT,
|
|
2686
|
+
MarketType.PERP,
|
|
2687
|
+
2, // orderId
|
|
2688
|
+
marketIndex,
|
|
2689
|
+
new BN(0).mul(PRICE_PRECISION), // price, ignored since it's a floating limit
|
|
2690
|
+
BASE_PRECISION, // quantity
|
|
2691
|
+
PositionDirection.LONG,
|
|
2692
|
+
vBid,
|
|
2693
|
+
vAsk,
|
|
2694
|
+
new BN(slot),
|
|
2695
|
+
ZERO,
|
|
2696
|
+
new BN(-4).mul(PRICE_PRECISION) // second best bid, but worse than vBid (8): 7.5
|
|
2697
|
+
);
|
|
2698
|
+
insertOrderToDLOB(
|
|
2699
|
+
dlob,
|
|
2700
|
+
user1.publicKey,
|
|
2701
|
+
OrderType.LIMIT,
|
|
2702
|
+
MarketType.PERP,
|
|
2703
|
+
3, // orderId
|
|
2704
|
+
marketIndex,
|
|
2705
|
+
new BN(0).mul(PRICE_PRECISION), // price; ignored since it's a floating limit
|
|
2706
|
+
new BN(1).mul(BASE_PRECISION), // quantity
|
|
2707
|
+
PositionDirection.LONG,
|
|
2708
|
+
vBid,
|
|
2709
|
+
vAsk,
|
|
2710
|
+
new BN(slot),
|
|
2711
|
+
ZERO,
|
|
2712
|
+
new BN(-1).mul(PRICE_PRECISION) // best price: 10.5
|
|
2713
|
+
);
|
|
2714
|
+
insertOrderToDLOB(
|
|
2715
|
+
dlob,
|
|
2716
|
+
user2.publicKey,
|
|
2717
|
+
OrderType.LIMIT,
|
|
2718
|
+
MarketType.PERP,
|
|
2719
|
+
1, // orderId
|
|
2720
|
+
marketIndex,
|
|
2721
|
+
new BN(0).mul(PRICE_PRECISION), // price; ignored since it's a floating limit
|
|
2722
|
+
BASE_PRECISION, // quantity
|
|
2723
|
+
PositionDirection.LONG,
|
|
2724
|
+
vBid,
|
|
2725
|
+
vAsk,
|
|
2726
|
+
new BN(slot),
|
|
2727
|
+
ZERO,
|
|
2728
|
+
new BN(-5).mul(PRICE_PRECISION) // third best bid, worse than vBid (8): 6.5
|
|
2729
|
+
);
|
|
2730
|
+
|
|
2731
|
+
console.log('DLOB state before fill:');
|
|
2732
|
+
printBookState(dlob, marketIndex, vBid, vAsk, slot, oracle);
|
|
2733
|
+
|
|
2734
|
+
const nodesToFillBefore = dlob.findNodesToFill(
|
|
2735
|
+
marketIndex,
|
|
2736
|
+
vBid,
|
|
2737
|
+
vAsk,
|
|
2738
|
+
slot,
|
|
2739
|
+
ts,
|
|
2740
|
+
MarketType.PERP,
|
|
2741
|
+
oracle
|
|
2742
|
+
);
|
|
2743
|
+
expect(nodesToFillBefore.length).to.equal(0);
|
|
2744
|
+
|
|
2745
|
+
// place two market sell orders to eat the best bid
|
|
2746
|
+
insertOrderToDLOB(
|
|
2747
|
+
dlob,
|
|
2748
|
+
user3.publicKey,
|
|
2749
|
+
OrderType.MARKET,
|
|
2750
|
+
MarketType.PERP,
|
|
2751
|
+
4, // orderId
|
|
2752
|
+
marketIndex,
|
|
2753
|
+
new BN(5).mul(PRICE_PRECISION), // price
|
|
2754
|
+
new BN(1).mul(BASE_PRECISION), // quantity, should consume best bid floating limit
|
|
2755
|
+
PositionDirection.SHORT,
|
|
2756
|
+
vBid,
|
|
2757
|
+
vAsk,
|
|
2758
|
+
new BN(slot)
|
|
2759
|
+
);
|
|
2760
|
+
insertOrderToDLOB(
|
|
2761
|
+
dlob,
|
|
2762
|
+
user4.publicKey,
|
|
2763
|
+
OrderType.MARKET,
|
|
2764
|
+
MarketType.PERP,
|
|
2765
|
+
5, // orderId
|
|
2766
|
+
marketIndex,
|
|
2767
|
+
// new BN(8).mul(PRICE_PRECISION), // price, this price will fill with vamm
|
|
2768
|
+
new BN(4).mul(PRICE_PRECISION), // price, this SHOULD fill with vamm
|
|
2769
|
+
new BN(2).mul(BASE_PRECISION), // quantity, should be filled with next best bid (vAMM)
|
|
2770
|
+
PositionDirection.SHORT,
|
|
2771
|
+
vBid,
|
|
2772
|
+
vAsk,
|
|
2773
|
+
new BN(slot)
|
|
2774
|
+
);
|
|
2775
|
+
|
|
2776
|
+
// auction ends, but order not expired
|
|
2777
|
+
const afterAuctionSlot = slot + 11;
|
|
2778
|
+
const afterAuctionTs = ts + 11;
|
|
2779
|
+
printBookState(dlob, marketIndex, vBid, vAsk, afterAuctionSlot, oracle);
|
|
2780
|
+
|
|
2781
|
+
const nodesToFillAfter = dlob.findNodesToFill(
|
|
2782
|
+
marketIndex,
|
|
2783
|
+
vBid,
|
|
2784
|
+
vAsk,
|
|
2785
|
+
afterAuctionSlot,
|
|
2786
|
+
afterAuctionTs,
|
|
2787
|
+
MarketType.PERP,
|
|
2788
|
+
oracle
|
|
2789
|
+
);
|
|
2790
|
+
|
|
2791
|
+
console.log(`Filled nodes: ${nodesToFillAfter.length}`);
|
|
2792
|
+
for (const n of nodesToFillAfter) {
|
|
2793
|
+
printCrossedNodes(n, afterAuctionSlot);
|
|
2794
|
+
}
|
|
2795
|
+
|
|
2796
|
+
// taker should fill first order completely with best maker (1/1)
|
|
2797
|
+
expect(
|
|
2798
|
+
nodesToFillAfter[0].node.order?.orderId,
|
|
2799
|
+
'wrong taker orderId'
|
|
2800
|
+
).to.equal(4);
|
|
2801
|
+
expect(
|
|
2802
|
+
nodesToFillAfter[0].makerNode?.order?.orderId,
|
|
2803
|
+
'wrong maker orderId'
|
|
2804
|
+
).to.equal(3);
|
|
2805
|
+
|
|
2806
|
+
// taker should fill second order completely with vamm
|
|
2807
|
+
expect(
|
|
2808
|
+
nodesToFillAfter[1].node.order?.orderId,
|
|
2809
|
+
'wrong taker orderId'
|
|
2810
|
+
).to.equal(5);
|
|
2811
|
+
expect(
|
|
2812
|
+
nodesToFillAfter[1].makerNode?.order?.orderId,
|
|
2813
|
+
'wrong maker orderId'
|
|
2814
|
+
).to.equal(2); // filler should match the DLOB makers, protocol will fill the taker with vAMM if it offers a better price.
|
|
2815
|
+
|
|
2816
|
+
expect(nodesToFillAfter.length).to.equal(3);
|
|
2817
|
+
});
|
|
2818
|
+
|
|
2819
|
+
it('Test fills crossing bids with vAMM after auction ends', () => {
|
|
2820
|
+
const vAsk = new BN(15).mul(PRICE_PRECISION);
|
|
2821
|
+
const vBid = new BN(8).mul(PRICE_PRECISION);
|
|
2822
|
+
|
|
2823
|
+
const mockUserMap = new MockUserMap();
|
|
2824
|
+
const user0 = Keypair.generate();
|
|
2825
|
+
const user0Auth = Keypair.generate();
|
|
2826
|
+
const user1 = Keypair.generate();
|
|
2827
|
+
const user1Auth = Keypair.generate();
|
|
2828
|
+
const user2 = Keypair.generate();
|
|
2829
|
+
const user2Auth = Keypair.generate();
|
|
2830
|
+
mockUserMap.addUserAccountAuthority(user0.publicKey, user0Auth.publicKey);
|
|
2831
|
+
mockUserMap.addUserAccountAuthority(user1.publicKey, user1Auth.publicKey);
|
|
2832
|
+
mockUserMap.addUserAccountAuthority(user2.publicKey, user2Auth.publicKey);
|
|
2833
|
+
|
|
2834
|
+
const dlob = new DLOB(
|
|
2835
|
+
mockPerpMarkets,
|
|
2836
|
+
mockSpotMarkets,
|
|
2837
|
+
mockStateAccount,
|
|
2838
|
+
mockUserMap,
|
|
2839
|
+
false
|
|
2840
|
+
);
|
|
2841
|
+
const marketIndex = 0;
|
|
2842
|
+
|
|
2843
|
+
const slot = 12;
|
|
2844
|
+
const ts = 12;
|
|
2845
|
+
const oracle = {
|
|
2846
|
+
price: vBid.add(vAsk).div(new BN(2)), // 11.5
|
|
2847
|
+
slot: new BN(slot),
|
|
2848
|
+
confidence: new BN(1),
|
|
2849
|
+
hasSufficientNumberOfDataPoints: true,
|
|
2850
|
+
};
|
|
2851
|
+
|
|
2852
|
+
// insert some floating limit buy below vAMM bid
|
|
2853
|
+
insertOrderToDLOB(
|
|
2854
|
+
dlob,
|
|
2855
|
+
user0.publicKey,
|
|
2856
|
+
OrderType.LIMIT,
|
|
2857
|
+
MarketType.PERP,
|
|
2858
|
+
2, // orderId
|
|
2859
|
+
marketIndex,
|
|
2860
|
+
new BN(17).mul(PRICE_PRECISION), // price, crosses vAsk
|
|
2861
|
+
BASE_PRECISION, // quantity
|
|
2862
|
+
PositionDirection.LONG,
|
|
2863
|
+
vBid,
|
|
2864
|
+
vAsk,
|
|
2865
|
+
new BN(slot),
|
|
2866
|
+
new BN(200)
|
|
2867
|
+
);
|
|
2868
|
+
insertOrderToDLOB(
|
|
2869
|
+
dlob,
|
|
2870
|
+
user1.publicKey,
|
|
2871
|
+
OrderType.LIMIT,
|
|
2872
|
+
MarketType.PERP,
|
|
2873
|
+
3, // orderId
|
|
2874
|
+
marketIndex,
|
|
2875
|
+
new BN(19).mul(PRICE_PRECISION), // price; crosses vAsk
|
|
2876
|
+
new BN(1).mul(BASE_PRECISION), // quantity
|
|
2877
|
+
PositionDirection.LONG,
|
|
2878
|
+
vBid,
|
|
2879
|
+
vAsk,
|
|
2880
|
+
new BN(slot),
|
|
2881
|
+
new BN(200)
|
|
2882
|
+
);
|
|
2883
|
+
insertOrderToDLOB(
|
|
2884
|
+
dlob,
|
|
2885
|
+
user2.publicKey,
|
|
2886
|
+
OrderType.LIMIT,
|
|
2887
|
+
MarketType.PERP,
|
|
2888
|
+
1, // orderId
|
|
2889
|
+
marketIndex,
|
|
2890
|
+
new BN(5).mul(PRICE_PRECISION), // price; doens't cross
|
|
2891
|
+
BASE_PRECISION, // quantity
|
|
2892
|
+
PositionDirection.LONG,
|
|
2893
|
+
vBid,
|
|
2894
|
+
vAsk,
|
|
2895
|
+
new BN(slot),
|
|
2896
|
+
new BN(200)
|
|
2897
|
+
);
|
|
2898
|
+
console.log(`Book state before fill:`);
|
|
2899
|
+
printBookState(dlob, marketIndex, vBid, vAsk, slot, oracle);
|
|
2900
|
+
|
|
2901
|
+
const nodesToFillBefore = dlob.findNodesToFill(
|
|
2902
|
+
marketIndex,
|
|
2903
|
+
vBid,
|
|
2904
|
+
vAsk,
|
|
2905
|
+
slot,
|
|
2906
|
+
ts,
|
|
2907
|
+
MarketType.PERP,
|
|
2908
|
+
oracle
|
|
2909
|
+
);
|
|
2910
|
+
expect(nodesToFillBefore.length).to.equal(0);
|
|
2911
|
+
|
|
2912
|
+
// auction ends now
|
|
2913
|
+
const afterAuctionSlot = 10 * slot;
|
|
2914
|
+
const afterAuctionTs = 10 * ts;
|
|
2915
|
+
|
|
2916
|
+
const nodesToFillAfter = dlob.findNodesToFill(
|
|
2917
|
+
marketIndex,
|
|
2918
|
+
vBid,
|
|
2919
|
+
vAsk,
|
|
2920
|
+
afterAuctionSlot,
|
|
2921
|
+
afterAuctionTs,
|
|
2922
|
+
MarketType.PERP,
|
|
2923
|
+
oracle
|
|
2924
|
+
);
|
|
2925
|
+
|
|
2926
|
+
console.log(`Book state after fill:`);
|
|
2927
|
+
printBookState(dlob, marketIndex, vBid, vAsk, afterAuctionSlot, oracle);
|
|
2928
|
+
|
|
2929
|
+
console.log(`Filled nodes: ${nodesToFillAfter.length}`);
|
|
2930
|
+
for (const n of nodesToFillAfter) {
|
|
2931
|
+
printCrossedNodes(n, afterAuctionSlot);
|
|
2932
|
+
}
|
|
2933
|
+
|
|
2934
|
+
// taker should fill first order completely with best maker (1/1)
|
|
2935
|
+
expect(
|
|
2936
|
+
nodesToFillAfter[0].node.order?.orderId,
|
|
2937
|
+
'wrong taker orderId'
|
|
2938
|
+
).to.equal(3);
|
|
2939
|
+
expect(
|
|
2940
|
+
nodesToFillAfter[0].makerNode?.order?.orderId,
|
|
2941
|
+
'wrong maker orderId'
|
|
2942
|
+
).to.equal(undefined);
|
|
2943
|
+
|
|
2944
|
+
// taker should fill second order completely with vamm
|
|
2945
|
+
expect(
|
|
2946
|
+
nodesToFillAfter[1].node.order?.orderId,
|
|
2947
|
+
'wrong taker orderId'
|
|
2948
|
+
).to.equal(2);
|
|
2949
|
+
expect(
|
|
2950
|
+
nodesToFillAfter[1].makerNode?.order?.orderId,
|
|
2951
|
+
'wrong maker orderId'
|
|
2952
|
+
).to.equal(undefined);
|
|
2953
|
+
|
|
2954
|
+
expect(nodesToFillAfter.length).to.equal(2);
|
|
2955
|
+
});
|
|
2956
|
+
|
|
2957
|
+
it('Test fills two limit orders better than vAmm', () => {
|
|
2958
|
+
const vAsk = new BN(20).mul(PRICE_PRECISION);
|
|
2959
|
+
const vBid = new BN(5).mul(PRICE_PRECISION);
|
|
2960
|
+
|
|
2961
|
+
const mockUserMap = new MockUserMap();
|
|
2962
|
+
const user0 = Keypair.generate();
|
|
2963
|
+
const user0Auth = Keypair.generate();
|
|
2964
|
+
const user1 = Keypair.generate();
|
|
2965
|
+
const user1Auth = Keypair.generate();
|
|
2966
|
+
mockUserMap.addUserAccountAuthority(user0.publicKey, user0Auth.publicKey);
|
|
2967
|
+
mockUserMap.addUserAccountAuthority(user1.publicKey, user1Auth.publicKey);
|
|
2968
|
+
|
|
2969
|
+
const dlob = new DLOB(
|
|
2970
|
+
mockPerpMarkets,
|
|
2971
|
+
mockSpotMarkets,
|
|
2972
|
+
mockStateAccount,
|
|
2973
|
+
mockUserMap,
|
|
2974
|
+
false
|
|
2975
|
+
);
|
|
2976
|
+
const marketIndex = 0;
|
|
2977
|
+
|
|
2978
|
+
const slot = 12;
|
|
2979
|
+
const ts = 12;
|
|
2980
|
+
const oracle = {
|
|
2981
|
+
price: vBid.add(vAsk).div(new BN(2)), // 11.5
|
|
2982
|
+
slot: new BN(slot),
|
|
2983
|
+
confidence: new BN(1),
|
|
2984
|
+
hasSufficientNumberOfDataPoints: true,
|
|
2985
|
+
};
|
|
2986
|
+
|
|
2987
|
+
// insert a sell below the bid, but above vBid
|
|
2988
|
+
insertOrderToDLOB(
|
|
2989
|
+
dlob,
|
|
2990
|
+
user0.publicKey,
|
|
2991
|
+
OrderType.LIMIT,
|
|
2992
|
+
MarketType.PERP,
|
|
2993
|
+
3, // orderId
|
|
2994
|
+
marketIndex,
|
|
2995
|
+
new BN(10).mul(PRICE_PRECISION), // price; crosses bid
|
|
2996
|
+
new BN(1).mul(BASE_PRECISION), // quantity
|
|
2997
|
+
PositionDirection.SHORT,
|
|
2998
|
+
vAsk,
|
|
2999
|
+
vBid,
|
|
3000
|
+
new BN(slot),
|
|
3001
|
+
new BN(200)
|
|
3002
|
+
);
|
|
3003
|
+
// insert a buy above the vBid
|
|
3004
|
+
insertOrderToDLOB(
|
|
3005
|
+
dlob,
|
|
3006
|
+
user1.publicKey,
|
|
3007
|
+
OrderType.LIMIT,
|
|
3008
|
+
MarketType.PERP,
|
|
3009
|
+
2, // orderId
|
|
3010
|
+
marketIndex,
|
|
3011
|
+
new BN(15).mul(PRICE_PRECISION), // price,
|
|
3012
|
+
new BN(8768).mul(BASE_PRECISION).div(new BN(10000)), // quantity
|
|
3013
|
+
PositionDirection.LONG,
|
|
3014
|
+
vBid,
|
|
3015
|
+
vAsk,
|
|
3016
|
+
new BN(slot + 1), // later order becomes taker
|
|
3017
|
+
new BN(200)
|
|
3018
|
+
);
|
|
3019
|
+
|
|
3020
|
+
console.log(`Book state before fill:`);
|
|
3021
|
+
printBookState(dlob, marketIndex, vBid, vAsk, slot, oracle);
|
|
3022
|
+
|
|
3023
|
+
const nodesToFillBefore = dlob.findNodesToFill(
|
|
3024
|
+
marketIndex,
|
|
3025
|
+
vBid,
|
|
3026
|
+
vAsk,
|
|
3027
|
+
slot,
|
|
3028
|
+
ts,
|
|
3029
|
+
MarketType.PERP,
|
|
3030
|
+
oracle
|
|
3031
|
+
);
|
|
3032
|
+
console.log(`Filled nodes: ${nodesToFillBefore.length}`);
|
|
3033
|
+
for (const n of nodesToFillBefore) {
|
|
3034
|
+
printCrossedNodes(n, slot);
|
|
3035
|
+
}
|
|
3036
|
+
expect(nodesToFillBefore.length).to.equal(1);
|
|
3037
|
+
|
|
3038
|
+
// first order is maker, second is taker
|
|
3039
|
+
expect(
|
|
3040
|
+
nodesToFillBefore[0].node.order?.orderId,
|
|
3041
|
+
'wrong taker orderId'
|
|
3042
|
+
).to.equal(2);
|
|
3043
|
+
expect(
|
|
3044
|
+
nodesToFillBefore[0].makerNode?.order?.orderId,
|
|
3045
|
+
'wrong maker orderId'
|
|
3046
|
+
).to.equal(3);
|
|
3047
|
+
});
|
|
3048
|
+
|
|
3049
|
+
it('Test will not fill two limit orders by same authority', () => {
|
|
3050
|
+
const vAsk = new BN(20).mul(PRICE_PRECISION);
|
|
3051
|
+
const vBid = new BN(5).mul(PRICE_PRECISION);
|
|
3052
|
+
|
|
3053
|
+
const mockUserMap = new MockUserMap();
|
|
3054
|
+
const user0 = Keypair.generate();
|
|
3055
|
+
const user0Auth = Keypair.generate();
|
|
3056
|
+
const user1 = Keypair.generate();
|
|
3057
|
+
mockUserMap.addUserAccountAuthority(user0.publicKey, user0Auth.publicKey);
|
|
3058
|
+
mockUserMap.addUserAccountAuthority(user1.publicKey, user0Auth.publicKey);
|
|
3059
|
+
|
|
3060
|
+
const dlob = new DLOB(
|
|
3061
|
+
mockPerpMarkets,
|
|
3062
|
+
mockSpotMarkets,
|
|
3063
|
+
mockStateAccount,
|
|
3064
|
+
mockUserMap,
|
|
3065
|
+
false
|
|
3066
|
+
);
|
|
3067
|
+
const marketIndex = 0;
|
|
3068
|
+
|
|
3069
|
+
const slot = 12;
|
|
3070
|
+
const ts = 12;
|
|
3071
|
+
const oracle = {
|
|
3072
|
+
price: vBid.add(vAsk).div(new BN(2)), // 11.5
|
|
3073
|
+
slot: new BN(slot),
|
|
3074
|
+
confidence: new BN(1),
|
|
3075
|
+
hasSufficientNumberOfDataPoints: true,
|
|
3076
|
+
};
|
|
3077
|
+
|
|
3078
|
+
// insert a sell below the bid, but above vBid
|
|
3079
|
+
insertOrderToDLOB(
|
|
3080
|
+
dlob,
|
|
3081
|
+
user0.publicKey,
|
|
3082
|
+
OrderType.LIMIT,
|
|
3083
|
+
MarketType.PERP,
|
|
3084
|
+
3, // orderId
|
|
3085
|
+
marketIndex,
|
|
3086
|
+
new BN(10).mul(PRICE_PRECISION), // price; crosses bid
|
|
3087
|
+
new BN(1).mul(BASE_PRECISION), // quantity
|
|
3088
|
+
PositionDirection.SHORT,
|
|
3089
|
+
vAsk,
|
|
3090
|
+
vBid,
|
|
3091
|
+
new BN(slot),
|
|
3092
|
+
new BN(200)
|
|
3093
|
+
);
|
|
3094
|
+
// insert a buy above the vBid
|
|
3095
|
+
insertOrderToDLOB(
|
|
3096
|
+
dlob,
|
|
3097
|
+
user1.publicKey,
|
|
3098
|
+
OrderType.LIMIT,
|
|
3099
|
+
MarketType.PERP,
|
|
3100
|
+
2, // orderId
|
|
3101
|
+
marketIndex,
|
|
3102
|
+
new BN(15).mul(PRICE_PRECISION), // price,
|
|
3103
|
+
new BN(8768).mul(BASE_PRECISION).div(new BN(10000)), // quantity
|
|
3104
|
+
PositionDirection.LONG,
|
|
3105
|
+
vBid,
|
|
3106
|
+
vAsk,
|
|
3107
|
+
new BN(slot),
|
|
3108
|
+
new BN(200)
|
|
3109
|
+
);
|
|
3110
|
+
|
|
3111
|
+
console.log(`Book state before fill:`);
|
|
3112
|
+
printBookState(dlob, marketIndex, vBid, vAsk, slot, oracle);
|
|
3113
|
+
|
|
3114
|
+
const nodesToFillBefore = dlob.findNodesToFill(
|
|
3115
|
+
marketIndex,
|
|
3116
|
+
vBid,
|
|
3117
|
+
vAsk,
|
|
3118
|
+
slot,
|
|
3119
|
+
ts,
|
|
3120
|
+
MarketType.PERP,
|
|
3121
|
+
oracle
|
|
3122
|
+
);
|
|
3123
|
+
console.log(`Filled nodes: ${nodesToFillBefore.length}`);
|
|
3124
|
+
for (const n of nodesToFillBefore) {
|
|
3125
|
+
printCrossedNodes(n, slot);
|
|
3126
|
+
}
|
|
3127
|
+
|
|
3128
|
+
expect(nodesToFillBefore.length).to.equal(0);
|
|
3129
|
+
});
|
|
3130
|
+
});
|
|
3131
|
+
|
|
3132
|
+
describe('DLOB Spot Tests', () => {
|
|
3133
|
+
it('Test proper bids', () => {
|
|
3134
|
+
const vAsk = new BN(115);
|
|
3135
|
+
const vBid = new BN(100);
|
|
3136
|
+
const mockUserMap = new MockUserMap();
|
|
3137
|
+
const dlob = new DLOB(
|
|
3138
|
+
mockPerpMarkets,
|
|
3139
|
+
mockSpotMarkets,
|
|
3140
|
+
mockStateAccount,
|
|
3141
|
+
mockUserMap,
|
|
3142
|
+
false
|
|
3143
|
+
);
|
|
3144
|
+
const marketIndex = 0;
|
|
3145
|
+
|
|
3146
|
+
const slot = 12;
|
|
3147
|
+
const oracle = {
|
|
3148
|
+
price: vBid.add(vAsk).div(new BN(2)),
|
|
3149
|
+
slot: new BN(slot),
|
|
3150
|
+
confidence: new BN(1),
|
|
3151
|
+
hasSufficientNumberOfDataPoints: true,
|
|
3152
|
+
};
|
|
3153
|
+
const testCases = [
|
|
3154
|
+
{
|
|
3155
|
+
expectedIdx: 3,
|
|
3156
|
+
orderId: 5,
|
|
3157
|
+
price: new BN(0), // will calc 108
|
|
3158
|
+
direction: PositionDirection.LONG,
|
|
3159
|
+
orderType: OrderType.MARKET,
|
|
3160
|
+
},
|
|
3161
|
+
{
|
|
3162
|
+
expectedIdx: 4,
|
|
3163
|
+
orderId: 6,
|
|
3164
|
+
price: new BN(0), // will calc 108
|
|
3165
|
+
direction: PositionDirection.LONG,
|
|
3166
|
+
orderType: OrderType.MARKET,
|
|
3167
|
+
},
|
|
3168
|
+
{
|
|
3169
|
+
expectedIdx: 5,
|
|
3170
|
+
orderId: 7,
|
|
3171
|
+
price: new BN(0), // will calc 108
|
|
3172
|
+
direction: PositionDirection.LONG,
|
|
3173
|
+
orderType: OrderType.MARKET,
|
|
3174
|
+
},
|
|
3175
|
+
{
|
|
3176
|
+
expectedIdx: 0,
|
|
3177
|
+
orderId: 1,
|
|
3178
|
+
price: new BN(110),
|
|
3179
|
+
direction: PositionDirection.LONG,
|
|
3180
|
+
orderType: OrderType.LIMIT,
|
|
3181
|
+
},
|
|
3182
|
+
{
|
|
3183
|
+
expectedIdx: 1,
|
|
3184
|
+
orderId: 2,
|
|
3185
|
+
price: new BN(109),
|
|
3186
|
+
direction: PositionDirection.LONG,
|
|
3187
|
+
orderType: OrderType.LIMIT,
|
|
3188
|
+
},
|
|
3189
|
+
{
|
|
3190
|
+
expectedIdx: 6,
|
|
3191
|
+
orderId: 3,
|
|
3192
|
+
price: new BN(107),
|
|
3193
|
+
direction: PositionDirection.LONG,
|
|
3194
|
+
orderType: OrderType.LIMIT,
|
|
3195
|
+
},
|
|
3196
|
+
{
|
|
3197
|
+
expectedIdx: 7,
|
|
3198
|
+
orderId: 4,
|
|
3199
|
+
price: new BN(106),
|
|
3200
|
+
direction: PositionDirection.LONG,
|
|
3201
|
+
orderType: OrderType.LIMIT,
|
|
3202
|
+
},
|
|
3203
|
+
];
|
|
3204
|
+
|
|
3205
|
+
for (const t of testCases) {
|
|
3206
|
+
const user0 = Keypair.generate();
|
|
3207
|
+
const user0Auth = Keypair.generate();
|
|
3208
|
+
mockUserMap.addUserAccountAuthority(user0.publicKey, user0Auth.publicKey);
|
|
3209
|
+
|
|
3210
|
+
insertOrderToDLOB(
|
|
3211
|
+
dlob,
|
|
3212
|
+
user0.publicKey,
|
|
3213
|
+
t.orderType || OrderType.LIMIT,
|
|
3214
|
+
MarketType.SPOT,
|
|
3215
|
+
t.orderId || 0, // orderId
|
|
3216
|
+
marketIndex,
|
|
3217
|
+
t.price || new BN(0), // price
|
|
3218
|
+
BASE_PRECISION, // quantity
|
|
3219
|
+
t.direction || PositionDirection.LONG,
|
|
3220
|
+
vBid,
|
|
3221
|
+
vAsk
|
|
3222
|
+
);
|
|
3223
|
+
}
|
|
3224
|
+
|
|
3225
|
+
const expectedTestCase = testCases.sort((a, b) => {
|
|
3226
|
+
// ascending order
|
|
3227
|
+
return a.expectedIdx - b.expectedIdx;
|
|
3228
|
+
});
|
|
3229
|
+
const bids = dlob.getBids(
|
|
3230
|
+
marketIndex,
|
|
3231
|
+
undefined,
|
|
3232
|
+
slot,
|
|
3233
|
+
MarketType.SPOT,
|
|
3234
|
+
oracle
|
|
3235
|
+
);
|
|
3236
|
+
|
|
3237
|
+
console.log('The Book Bids:');
|
|
3238
|
+
let countBids = 0;
|
|
3239
|
+
for (const bid of bids) {
|
|
3240
|
+
printOrderNode(bid, oracle, slot);
|
|
3241
|
+
|
|
3242
|
+
expect(bid.order?.orderId).to.equal(expectedTestCase[countBids].orderId);
|
|
3243
|
+
expect(bid.order?.price.toNumber()).to.equal(
|
|
3244
|
+
expectedTestCase[countBids].price?.toNumber()
|
|
3245
|
+
);
|
|
3246
|
+
expect(bid.order?.direction).to.equal(
|
|
3247
|
+
expectedTestCase[countBids].direction
|
|
3248
|
+
);
|
|
3249
|
+
expect(bid.order?.orderType).to.equal(
|
|
3250
|
+
expectedTestCase[countBids].orderType
|
|
3251
|
+
);
|
|
3252
|
+
countBids++;
|
|
3253
|
+
}
|
|
3254
|
+
|
|
3255
|
+
expect(countBids).to.equal(testCases.length);
|
|
3256
|
+
});
|
|
3257
|
+
|
|
3258
|
+
it('Test proper bids on multiple markets', () => {
|
|
3259
|
+
const vAsk = new BN(15);
|
|
3260
|
+
const vBid = new BN(10);
|
|
3261
|
+
const mockUserMap = new MockUserMap();
|
|
3262
|
+
const dlob = new DLOB(
|
|
3263
|
+
mockPerpMarkets,
|
|
3264
|
+
mockSpotMarkets,
|
|
3265
|
+
mockStateAccount,
|
|
3266
|
+
mockUserMap,
|
|
3267
|
+
false
|
|
3268
|
+
);
|
|
3269
|
+
const marketIndex0 = 0;
|
|
3270
|
+
const marketIndex1 = 1;
|
|
3271
|
+
|
|
3272
|
+
const slot = 12;
|
|
3273
|
+
const oracle = {
|
|
3274
|
+
price: vBid.add(vAsk).div(new BN(2)),
|
|
3275
|
+
slot: new BN(slot),
|
|
3276
|
+
confidence: new BN(1),
|
|
3277
|
+
hasSufficientNumberOfDataPoints: true,
|
|
3278
|
+
};
|
|
3279
|
+
const testCases = [
|
|
3280
|
+
{
|
|
3281
|
+
expectedIdx: 3,
|
|
3282
|
+
orderId: 5,
|
|
3283
|
+
price: new BN(0),
|
|
3284
|
+
direction: PositionDirection.LONG,
|
|
3285
|
+
orderType: OrderType.MARKET,
|
|
3286
|
+
marketIndex: marketIndex0,
|
|
3287
|
+
},
|
|
3288
|
+
{
|
|
3289
|
+
expectedIdx: 4,
|
|
3290
|
+
orderId: 6,
|
|
3291
|
+
price: new BN(0),
|
|
3292
|
+
direction: PositionDirection.LONG,
|
|
3293
|
+
orderType: OrderType.MARKET,
|
|
3294
|
+
marketIndex: marketIndex0,
|
|
3295
|
+
},
|
|
3296
|
+
{
|
|
3297
|
+
expectedIdx: 5,
|
|
3298
|
+
orderId: 7,
|
|
3299
|
+
price: new BN(0),
|
|
3300
|
+
direction: PositionDirection.LONG,
|
|
3301
|
+
orderType: OrderType.MARKET,
|
|
3302
|
+
marketIndex: marketIndex1,
|
|
3303
|
+
},
|
|
3304
|
+
{
|
|
3305
|
+
expectedIdx: 0,
|
|
3306
|
+
orderId: 1,
|
|
3307
|
+
price: new BN(12),
|
|
3308
|
+
direction: PositionDirection.LONG,
|
|
3309
|
+
orderType: OrderType.LIMIT,
|
|
3310
|
+
marketIndex: marketIndex0,
|
|
3311
|
+
},
|
|
3312
|
+
{
|
|
3313
|
+
expectedIdx: 1,
|
|
3314
|
+
orderId: 2,
|
|
3315
|
+
price: new BN(11),
|
|
3316
|
+
direction: PositionDirection.LONG,
|
|
3317
|
+
orderType: OrderType.LIMIT,
|
|
3318
|
+
marketIndex: marketIndex0,
|
|
3319
|
+
},
|
|
3320
|
+
{
|
|
3321
|
+
expectedIdx: 7,
|
|
3322
|
+
orderId: 3,
|
|
3323
|
+
price: new BN(8),
|
|
3324
|
+
direction: PositionDirection.LONG,
|
|
3325
|
+
orderType: OrderType.LIMIT,
|
|
3326
|
+
marketIndex: marketIndex0,
|
|
3327
|
+
},
|
|
3328
|
+
{
|
|
3329
|
+
expectedIdx: 6,
|
|
3330
|
+
orderId: 4,
|
|
3331
|
+
price: new BN(9),
|
|
3332
|
+
direction: PositionDirection.LONG,
|
|
3333
|
+
orderType: OrderType.LIMIT,
|
|
3334
|
+
marketIndex: marketIndex1,
|
|
3335
|
+
},
|
|
3336
|
+
];
|
|
3337
|
+
|
|
3338
|
+
for (const t of testCases) {
|
|
3339
|
+
const user0 = Keypair.generate();
|
|
3340
|
+
const user0Auth = Keypair.generate();
|
|
3341
|
+
mockUserMap.addUserAccountAuthority(user0.publicKey, user0Auth.publicKey);
|
|
3342
|
+
|
|
3343
|
+
insertOrderToDLOB(
|
|
3344
|
+
dlob,
|
|
3345
|
+
user0.publicKey,
|
|
3346
|
+
t.orderType || OrderType.LIMIT,
|
|
3347
|
+
MarketType.SPOT,
|
|
3348
|
+
t.orderId || 0, // orderId
|
|
3349
|
+
t.marketIndex,
|
|
3350
|
+
t.price || new BN(0), // price
|
|
3351
|
+
BASE_PRECISION, // quantity
|
|
3352
|
+
t.direction || PositionDirection.LONG,
|
|
3353
|
+
vBid,
|
|
3354
|
+
vAsk
|
|
3355
|
+
);
|
|
3356
|
+
}
|
|
3357
|
+
|
|
3358
|
+
const bids0 = dlob.getBids(
|
|
3359
|
+
marketIndex0,
|
|
3360
|
+
vBid,
|
|
3361
|
+
slot,
|
|
3362
|
+
MarketType.SPOT,
|
|
3363
|
+
oracle
|
|
3364
|
+
);
|
|
3365
|
+
let countBids0 = 0;
|
|
3366
|
+
for (const bid of bids0) {
|
|
3367
|
+
console.log(
|
|
3368
|
+
` . vAMMNode? ${bid.isVammNode()}, ${JSON.stringify(
|
|
3369
|
+
bid.order?.orderType
|
|
3370
|
+
)} , ${bid.order?.orderId.toString()} , vammTestgetPRice: ${bid.getPrice(
|
|
3371
|
+
oracle,
|
|
3372
|
+
slot
|
|
3373
|
+
)}, price: ${bid.order?.price.toString()}, quantity: ${bid.order?.baseAssetAmountFilled.toString()}/${bid.order?.baseAssetAmount.toString()}`
|
|
3374
|
+
);
|
|
3375
|
+
countBids0++;
|
|
3376
|
+
}
|
|
3377
|
+
expect(countBids0).to.equal(5);
|
|
3378
|
+
|
|
3379
|
+
const bids1 = dlob.getBids(
|
|
3380
|
+
marketIndex1,
|
|
3381
|
+
vBid,
|
|
3382
|
+
slot,
|
|
3383
|
+
MarketType.SPOT,
|
|
3384
|
+
oracle
|
|
3385
|
+
);
|
|
3386
|
+
let countBids1 = 0;
|
|
3387
|
+
for (const bid of bids1) {
|
|
3388
|
+
console.log(
|
|
3389
|
+
` . vAMMNode? ${bid.isVammNode()}, ${JSON.stringify(
|
|
3390
|
+
bid.order?.orderType
|
|
3391
|
+
)} , ${bid.order?.orderId.toString()} , vammTestgetPRice: ${bid.getPrice(
|
|
3392
|
+
oracle,
|
|
3393
|
+
slot
|
|
3394
|
+
)}, price: ${bid.order?.price.toString()}, quantity: ${bid.order?.baseAssetAmountFilled.toString()}/${bid.order?.baseAssetAmount.toString()}`
|
|
3395
|
+
);
|
|
3396
|
+
|
|
3397
|
+
countBids1++;
|
|
3398
|
+
}
|
|
3399
|
+
expect(countBids1).to.equal(2);
|
|
3400
|
+
});
|
|
3401
|
+
|
|
3402
|
+
it('Test proper asks', () => {
|
|
3403
|
+
const vAsk = new BN(15);
|
|
3404
|
+
const vBid = new BN(10);
|
|
3405
|
+
const mockUserMap = new MockUserMap();
|
|
3406
|
+
const dlob = new DLOB(
|
|
3407
|
+
mockPerpMarkets,
|
|
3408
|
+
mockSpotMarkets,
|
|
3409
|
+
mockStateAccount,
|
|
3410
|
+
mockUserMap,
|
|
3411
|
+
false
|
|
3412
|
+
);
|
|
3413
|
+
const marketIndex = 0;
|
|
3414
|
+
|
|
3415
|
+
const slot = 12;
|
|
3416
|
+
const oracle = {
|
|
3417
|
+
price: vBid.add(vAsk).div(new BN(2)),
|
|
3418
|
+
slot: new BN(slot),
|
|
3419
|
+
confidence: new BN(1),
|
|
3420
|
+
hasSufficientNumberOfDataPoints: true,
|
|
3421
|
+
};
|
|
3422
|
+
const testCases = [
|
|
3423
|
+
{
|
|
3424
|
+
expectedIdx: 0,
|
|
3425
|
+
orderId: 3,
|
|
3426
|
+
price: new BN(0),
|
|
3427
|
+
direction: PositionDirection.SHORT,
|
|
3428
|
+
orderType: OrderType.MARKET,
|
|
3429
|
+
},
|
|
3430
|
+
{
|
|
3431
|
+
expectedIdx: 1,
|
|
3432
|
+
orderId: 4,
|
|
3433
|
+
price: new BN(0),
|
|
3434
|
+
direction: PositionDirection.SHORT,
|
|
3435
|
+
orderType: OrderType.MARKET,
|
|
3436
|
+
},
|
|
3437
|
+
{
|
|
3438
|
+
expectedIdx: 2,
|
|
3439
|
+
orderId: 5,
|
|
3440
|
+
price: new BN(0),
|
|
3441
|
+
direction: PositionDirection.SHORT,
|
|
3442
|
+
orderType: OrderType.MARKET,
|
|
3443
|
+
},
|
|
3444
|
+
{
|
|
3445
|
+
expectedIdx: 3,
|
|
3446
|
+
orderId: 1,
|
|
3447
|
+
price: new BN(13),
|
|
3448
|
+
direction: PositionDirection.SHORT,
|
|
3449
|
+
orderType: OrderType.LIMIT,
|
|
3450
|
+
},
|
|
3451
|
+
{
|
|
3452
|
+
expectedIdx: 6,
|
|
3453
|
+
orderId: 6,
|
|
3454
|
+
price: new BN(16),
|
|
3455
|
+
direction: PositionDirection.SHORT,
|
|
3456
|
+
orderType: OrderType.LIMIT,
|
|
3457
|
+
},
|
|
3458
|
+
{
|
|
3459
|
+
expectedIdx: 7,
|
|
3460
|
+
orderId: 7,
|
|
3461
|
+
price: new BN(17),
|
|
3462
|
+
direction: PositionDirection.SHORT,
|
|
3463
|
+
orderType: OrderType.LIMIT,
|
|
3464
|
+
},
|
|
3465
|
+
{
|
|
3466
|
+
expectedIdx: 4,
|
|
3467
|
+
orderId: 2,
|
|
3468
|
+
price: new BN(14),
|
|
3469
|
+
direction: PositionDirection.SHORT,
|
|
3470
|
+
orderType: OrderType.LIMIT,
|
|
3471
|
+
},
|
|
3472
|
+
];
|
|
3473
|
+
|
|
3474
|
+
for (const t of testCases) {
|
|
3475
|
+
const user0 = Keypair.generate();
|
|
3476
|
+
const user0Auth = Keypair.generate();
|
|
3477
|
+
mockUserMap.addUserAccountAuthority(user0.publicKey, user0Auth.publicKey);
|
|
3478
|
+
|
|
3479
|
+
insertOrderToDLOB(
|
|
3480
|
+
dlob,
|
|
3481
|
+
user0.publicKey,
|
|
3482
|
+
t.orderType || OrderType.LIMIT,
|
|
3483
|
+
MarketType.SPOT,
|
|
3484
|
+
t.orderId || 0, // orderId
|
|
3485
|
+
marketIndex,
|
|
3486
|
+
t.price || new BN(0), // price
|
|
3487
|
+
BASE_PRECISION, // quantity
|
|
3488
|
+
t.direction || PositionDirection.SHORT,
|
|
3489
|
+
vBid,
|
|
3490
|
+
vAsk
|
|
3491
|
+
);
|
|
3492
|
+
}
|
|
3493
|
+
|
|
3494
|
+
const expectedTestCase = testCases.sort((a, b) => {
|
|
3495
|
+
// ascending order
|
|
3496
|
+
return a.expectedIdx - b.expectedIdx;
|
|
3497
|
+
});
|
|
3498
|
+
const asks = dlob.getAsks(marketIndex, vAsk, slot, MarketType.SPOT, oracle);
|
|
3499
|
+
|
|
3500
|
+
console.log('The Book Asks:');
|
|
3501
|
+
let countAsks = 0;
|
|
3502
|
+
for (const ask of asks) {
|
|
3503
|
+
console.log(
|
|
3504
|
+
` . vAMMNode? ${ask.isVammNode()}, ${JSON.stringify(
|
|
3505
|
+
ask.order?.orderType
|
|
3506
|
+
)} , ${ask.order?.orderId.toString()} , vammTestgetPRice: ${ask.getPrice(
|
|
3507
|
+
oracle,
|
|
3508
|
+
slot
|
|
3509
|
+
)}, price: ${ask.order?.price.toString()}, quantity: ${ask.order?.baseAssetAmountFilled.toString()}/${ask.order?.baseAssetAmount.toString()}`
|
|
3510
|
+
);
|
|
3511
|
+
|
|
3512
|
+
expect(ask.order?.orderId).to.equal(expectedTestCase[countAsks].orderId);
|
|
3513
|
+
expect(ask.order?.price.toNumber()).to.equal(
|
|
3514
|
+
expectedTestCase[countAsks].price?.toNumber()
|
|
3515
|
+
);
|
|
3516
|
+
expect(ask.order?.direction).to.equal(
|
|
3517
|
+
expectedTestCase[countAsks].direction
|
|
3518
|
+
);
|
|
3519
|
+
expect(ask.order?.orderType).to.equal(
|
|
3520
|
+
expectedTestCase[countAsks].orderType
|
|
3521
|
+
);
|
|
3522
|
+
countAsks++;
|
|
3523
|
+
}
|
|
3524
|
+
|
|
3525
|
+
expect(countAsks).to.equal(testCases.length);
|
|
3526
|
+
});
|
|
3527
|
+
|
|
3528
|
+
it('Test insert market orders', () => {
|
|
3529
|
+
const slot = 12;
|
|
3530
|
+
const vAsk = new BN(110);
|
|
3531
|
+
const vBid = new BN(100);
|
|
3532
|
+
const oracle = {
|
|
3533
|
+
price: vBid.add(vAsk).div(new BN(2)),
|
|
3534
|
+
slot: new BN(slot),
|
|
3535
|
+
confidence: new BN(1),
|
|
3536
|
+
hasSufficientNumberOfDataPoints: true,
|
|
3537
|
+
};
|
|
3538
|
+
const mockUserMap = new MockUserMap();
|
|
3539
|
+
const dlob = new DLOB(
|
|
3540
|
+
mockPerpMarkets,
|
|
3541
|
+
mockSpotMarkets,
|
|
3542
|
+
mockStateAccount,
|
|
3543
|
+
mockUserMap,
|
|
3544
|
+
false
|
|
3545
|
+
);
|
|
3546
|
+
const marketIndex = 0;
|
|
3547
|
+
|
|
3548
|
+
// 3 mkt buys
|
|
3549
|
+
for (let i = 0; i < 3; i++) {
|
|
3550
|
+
const user0 = Keypair.generate();
|
|
3551
|
+
const user0Auth = Keypair.generate();
|
|
3552
|
+
mockUserMap.addUserAccountAuthority(user0.publicKey, user0Auth.publicKey);
|
|
3553
|
+
|
|
3554
|
+
insertOrderToDLOB(
|
|
3555
|
+
dlob,
|
|
3556
|
+
user0.publicKey,
|
|
3557
|
+
OrderType.MARKET,
|
|
3558
|
+
MarketType.SPOT,
|
|
3559
|
+
i + 1,
|
|
3560
|
+
marketIndex,
|
|
3561
|
+
new BN(0),
|
|
3562
|
+
BASE_PRECISION,
|
|
3563
|
+
PositionDirection.LONG,
|
|
3564
|
+
vBid,
|
|
3565
|
+
vAsk
|
|
3566
|
+
);
|
|
3567
|
+
}
|
|
3568
|
+
|
|
3569
|
+
// 3 mkt sells
|
|
3570
|
+
for (let i = 0; i < 3; i++) {
|
|
3571
|
+
const user0 = Keypair.generate();
|
|
3572
|
+
const user0Auth = Keypair.generate();
|
|
3573
|
+
mockUserMap.addUserAccountAuthority(user0.publicKey, user0Auth.publicKey);
|
|
3574
|
+
|
|
3575
|
+
insertOrderToDLOB(
|
|
3576
|
+
dlob,
|
|
3577
|
+
user0.publicKey,
|
|
3578
|
+
OrderType.MARKET,
|
|
3579
|
+
MarketType.SPOT,
|
|
3580
|
+
i + 1,
|
|
3581
|
+
marketIndex,
|
|
3582
|
+
new BN(0),
|
|
3583
|
+
BASE_PRECISION,
|
|
3584
|
+
PositionDirection.SHORT,
|
|
3585
|
+
vBid,
|
|
3586
|
+
vAsk
|
|
3587
|
+
);
|
|
3588
|
+
}
|
|
3589
|
+
|
|
3590
|
+
let asks = 0;
|
|
3591
|
+
for (const ask of dlob.getAsks(
|
|
3592
|
+
marketIndex,
|
|
3593
|
+
vAsk,
|
|
3594
|
+
2,
|
|
3595
|
+
MarketType.SPOT,
|
|
3596
|
+
oracle
|
|
3597
|
+
)) {
|
|
3598
|
+
// vamm node is last in asks
|
|
3599
|
+
asks++;
|
|
3600
|
+
|
|
3601
|
+
if (ask.order) {
|
|
3602
|
+
// market orders
|
|
3603
|
+
expect(getVariant(ask.order?.status)).to.equal('open');
|
|
3604
|
+
expect(getVariant(ask.order?.orderType)).to.equal('market');
|
|
3605
|
+
expect(getVariant(ask.order?.direction)).to.equal('short');
|
|
3606
|
+
expect(ask.order?.orderId).to.equal(asks);
|
|
3607
|
+
}
|
|
3608
|
+
}
|
|
3609
|
+
expect(asks).to.equal(3);
|
|
3610
|
+
|
|
3611
|
+
let bids = 0;
|
|
3612
|
+
for (const bid of dlob.getBids(
|
|
3613
|
+
marketIndex,
|
|
3614
|
+
vBid,
|
|
3615
|
+
2,
|
|
3616
|
+
MarketType.SPOT,
|
|
3617
|
+
oracle
|
|
3618
|
+
)) {
|
|
3619
|
+
// market orders
|
|
3620
|
+
expect(getVariant(bid.order?.status)).to.equal('open');
|
|
3621
|
+
expect(getVariant(bid.order?.orderType)).to.equal('market');
|
|
3622
|
+
expect(getVariant(bid.order?.direction)).to.equal('long');
|
|
3623
|
+
expect(bid.order?.orderId).to.equal(bids + 1);
|
|
3624
|
+
bids++;
|
|
3625
|
+
}
|
|
3626
|
+
expect(bids).to.equal(3); // 3 orders
|
|
3627
|
+
});
|
|
3628
|
+
|
|
3629
|
+
it('Test insert limit orders', () => {
|
|
3630
|
+
const vAsk = new BN(110);
|
|
3631
|
+
const vBid = new BN(100);
|
|
3632
|
+
const slot = 12;
|
|
3633
|
+
const oracle = {
|
|
3634
|
+
price: vBid.add(vAsk).div(new BN(2)),
|
|
3635
|
+
slot: new BN(slot),
|
|
3636
|
+
confidence: new BN(1),
|
|
3637
|
+
hasSufficientNumberOfDataPoints: true,
|
|
3638
|
+
};
|
|
3639
|
+
|
|
3640
|
+
const mockUserMap = new MockUserMap();
|
|
3641
|
+
const user0 = Keypair.generate();
|
|
3642
|
+
const userAuth0 = Keypair.generate();
|
|
3643
|
+
const user1 = Keypair.generate();
|
|
3644
|
+
const userAuth1 = Keypair.generate();
|
|
3645
|
+
const user2 = Keypair.generate();
|
|
3646
|
+
const userAuth2 = Keypair.generate();
|
|
3647
|
+
const user3 = Keypair.generate();
|
|
3648
|
+
const userAuth3 = Keypair.generate();
|
|
3649
|
+
const user4 = Keypair.generate();
|
|
3650
|
+
const userAuth4 = Keypair.generate();
|
|
3651
|
+
const user5 = Keypair.generate();
|
|
3652
|
+
const userAuth5 = Keypair.generate();
|
|
3653
|
+
|
|
3654
|
+
mockUserMap.addUserAccountAuthority(user0.publicKey, userAuth0.publicKey);
|
|
3655
|
+
mockUserMap.addUserAccountAuthority(user1.publicKey, userAuth1.publicKey);
|
|
3656
|
+
mockUserMap.addUserAccountAuthority(user2.publicKey, userAuth2.publicKey);
|
|
3657
|
+
mockUserMap.addUserAccountAuthority(user3.publicKey, userAuth3.publicKey);
|
|
3658
|
+
mockUserMap.addUserAccountAuthority(user4.publicKey, userAuth4.publicKey);
|
|
3659
|
+
mockUserMap.addUserAccountAuthority(user5.publicKey, userAuth5.publicKey);
|
|
3660
|
+
|
|
3661
|
+
const dlob = new DLOB(
|
|
3662
|
+
mockPerpMarkets,
|
|
3663
|
+
mockSpotMarkets,
|
|
3664
|
+
mockStateAccount,
|
|
3665
|
+
mockUserMap,
|
|
3666
|
+
false
|
|
3667
|
+
);
|
|
3668
|
+
const marketIndex = 0;
|
|
3669
|
+
insertOrderToDLOB(
|
|
3670
|
+
dlob,
|
|
3671
|
+
user0.publicKey,
|
|
3672
|
+
OrderType.LIMIT,
|
|
3673
|
+
MarketType.SPOT,
|
|
3674
|
+
3, // orderId
|
|
3675
|
+
marketIndex,
|
|
3676
|
+
new BN(50),
|
|
3677
|
+
BASE_PRECISION,
|
|
3678
|
+
PositionDirection.LONG,
|
|
3679
|
+
vBid,
|
|
3680
|
+
vAsk
|
|
3681
|
+
);
|
|
3682
|
+
|
|
3683
|
+
insertOrderToDLOB(
|
|
3684
|
+
dlob,
|
|
3685
|
+
user1.publicKey,
|
|
3686
|
+
OrderType.LIMIT,
|
|
3687
|
+
MarketType.SPOT,
|
|
3688
|
+
2, // orderId
|
|
3689
|
+
marketIndex,
|
|
3690
|
+
new BN(60),
|
|
3691
|
+
BASE_PRECISION,
|
|
3692
|
+
PositionDirection.LONG,
|
|
3693
|
+
vBid,
|
|
3694
|
+
vAsk
|
|
3695
|
+
);
|
|
3696
|
+
|
|
3697
|
+
insertOrderToDLOB(
|
|
3698
|
+
dlob,
|
|
3699
|
+
user2.publicKey,
|
|
3700
|
+
OrderType.LIMIT,
|
|
3701
|
+
MarketType.SPOT,
|
|
3702
|
+
1, // orderId
|
|
3703
|
+
marketIndex,
|
|
3704
|
+
new BN(70),
|
|
3705
|
+
BASE_PRECISION,
|
|
3706
|
+
PositionDirection.LONG,
|
|
3707
|
+
vBid,
|
|
3708
|
+
vAsk
|
|
3709
|
+
);
|
|
3710
|
+
|
|
3711
|
+
insertOrderToDLOB(
|
|
3712
|
+
dlob,
|
|
3713
|
+
user3.publicKey,
|
|
3714
|
+
OrderType.LIMIT,
|
|
3715
|
+
MarketType.SPOT,
|
|
3716
|
+
1, // orderId
|
|
3717
|
+
marketIndex,
|
|
3718
|
+
new BN(120),
|
|
3719
|
+
BASE_PRECISION,
|
|
3720
|
+
PositionDirection.SHORT,
|
|
3721
|
+
vBid,
|
|
3722
|
+
vAsk
|
|
3723
|
+
);
|
|
3724
|
+
|
|
3725
|
+
insertOrderToDLOB(
|
|
3726
|
+
dlob,
|
|
3727
|
+
user4.publicKey,
|
|
3728
|
+
OrderType.LIMIT,
|
|
3729
|
+
MarketType.SPOT,
|
|
3730
|
+
2, // orderId
|
|
3731
|
+
marketIndex,
|
|
3732
|
+
new BN(130),
|
|
3733
|
+
BASE_PRECISION,
|
|
3734
|
+
PositionDirection.SHORT,
|
|
3735
|
+
vBid,
|
|
3736
|
+
vAsk
|
|
3737
|
+
);
|
|
3738
|
+
|
|
3739
|
+
insertOrderToDLOB(
|
|
3740
|
+
dlob,
|
|
3741
|
+
user5.publicKey,
|
|
3742
|
+
OrderType.LIMIT,
|
|
3743
|
+
MarketType.SPOT,
|
|
3744
|
+
3, // orderId
|
|
3745
|
+
marketIndex,
|
|
3746
|
+
new BN(140),
|
|
3747
|
+
BASE_PRECISION,
|
|
3748
|
+
PositionDirection.SHORT,
|
|
3749
|
+
vBid,
|
|
3750
|
+
vAsk
|
|
3751
|
+
);
|
|
3752
|
+
|
|
3753
|
+
let asks = 0;
|
|
3754
|
+
for (const ask of dlob.getAsks(
|
|
3755
|
+
marketIndex,
|
|
3756
|
+
vAsk,
|
|
3757
|
+
slot,
|
|
3758
|
+
MarketType.SPOT,
|
|
3759
|
+
oracle
|
|
3760
|
+
)) {
|
|
3761
|
+
if (!ask.order) {
|
|
3762
|
+
console.error('wtf ask vamm?');
|
|
3763
|
+
continue;
|
|
3764
|
+
}
|
|
3765
|
+
// market orders
|
|
3766
|
+
console.log(`ask price: ${ask.order?.price.toString()}`);
|
|
3767
|
+
expect(getVariant(ask.order?.status)).to.equal('open');
|
|
3768
|
+
expect(getVariant(ask.order?.orderType)).to.equal('limit');
|
|
3769
|
+
expect(getVariant(ask.order?.direction)).to.equal('short');
|
|
3770
|
+
expect(ask.order?.orderId).to.equal(asks + 1);
|
|
3771
|
+
expect(ask.order?.price.gt(vAsk)).to.equal(true);
|
|
3772
|
+
asks++;
|
|
3773
|
+
}
|
|
3774
|
+
expect(asks).to.equal(3);
|
|
3775
|
+
|
|
3776
|
+
let bids = 0;
|
|
3777
|
+
for (const bid of dlob.getBids(
|
|
3778
|
+
marketIndex,
|
|
3779
|
+
vBid,
|
|
3780
|
+
slot,
|
|
3781
|
+
MarketType.SPOT,
|
|
3782
|
+
oracle
|
|
3783
|
+
)) {
|
|
3784
|
+
if (!bid.order) {
|
|
3785
|
+
console.error('wtf bid vamm?');
|
|
3786
|
+
continue;
|
|
3787
|
+
}
|
|
3788
|
+
// market orders
|
|
3789
|
+
console.log(`bid price: ${bid.order?.price.toString()}`);
|
|
3790
|
+
expect(getVariant(bid.order?.status)).to.equal('open');
|
|
3791
|
+
expect(getVariant(bid.order?.orderType)).to.equal('limit');
|
|
3792
|
+
expect(getVariant(bid.order?.direction)).to.equal('long');
|
|
3793
|
+
expect(bid.order?.orderId).to.equal(bids + 1);
|
|
3794
|
+
expect(bid.order?.price.lt(vBid)).to.equal(true);
|
|
3795
|
+
bids++;
|
|
3796
|
+
}
|
|
3797
|
+
expect(bids).to.equal(3);
|
|
3798
|
+
});
|
|
3799
|
+
|
|
3800
|
+
it('Test multiple market orders fill with multiple limit orders', () => {
|
|
3801
|
+
const vAsk = new BN(15);
|
|
3802
|
+
const vBid = new BN(10);
|
|
3803
|
+
|
|
3804
|
+
const mockUserMap = new MockUserMap();
|
|
3805
|
+
const user0 = Keypair.generate();
|
|
3806
|
+
const userAuth0 = Keypair.generate();
|
|
3807
|
+
const user1 = Keypair.generate();
|
|
3808
|
+
const userAuth1 = Keypair.generate();
|
|
3809
|
+
const user2 = Keypair.generate();
|
|
3810
|
+
const userAuth2 = Keypair.generate();
|
|
3811
|
+
const user3 = Keypair.generate();
|
|
3812
|
+
const userAuth3 = Keypair.generate();
|
|
3813
|
+
const user4 = Keypair.generate();
|
|
3814
|
+
const userAuth4 = Keypair.generate();
|
|
3815
|
+
const user5 = Keypair.generate();
|
|
3816
|
+
const userAuth5 = Keypair.generate();
|
|
3817
|
+
|
|
3818
|
+
mockUserMap.addUserAccountAuthority(user0.publicKey, userAuth0.publicKey);
|
|
3819
|
+
mockUserMap.addUserAccountAuthority(user1.publicKey, userAuth1.publicKey);
|
|
3820
|
+
mockUserMap.addUserAccountAuthority(user2.publicKey, userAuth2.publicKey);
|
|
3821
|
+
mockUserMap.addUserAccountAuthority(user3.publicKey, userAuth3.publicKey);
|
|
3822
|
+
mockUserMap.addUserAccountAuthority(user4.publicKey, userAuth4.publicKey);
|
|
3823
|
+
mockUserMap.addUserAccountAuthority(user5.publicKey, userAuth5.publicKey);
|
|
3824
|
+
|
|
3825
|
+
const dlob = new DLOB(
|
|
3826
|
+
mockPerpMarkets,
|
|
3827
|
+
mockSpotMarkets,
|
|
3828
|
+
mockStateAccount,
|
|
3829
|
+
mockUserMap,
|
|
3830
|
+
false
|
|
3831
|
+
);
|
|
3832
|
+
const marketIndex = 0;
|
|
3833
|
+
|
|
3834
|
+
// insert some limit buys above vamm bid, below ask
|
|
3835
|
+
insertOrderToDLOB(
|
|
3836
|
+
dlob,
|
|
3837
|
+
user0.publicKey,
|
|
3838
|
+
OrderType.LIMIT,
|
|
3839
|
+
MarketType.SPOT,
|
|
3840
|
+
1, // orderId
|
|
3841
|
+
marketIndex,
|
|
3842
|
+
new BN(11), // price
|
|
3843
|
+
BASE_PRECISION, // quantity
|
|
3844
|
+
PositionDirection.LONG,
|
|
3845
|
+
vBid,
|
|
3846
|
+
vAsk
|
|
3847
|
+
);
|
|
3848
|
+
insertOrderToDLOB(
|
|
3849
|
+
dlob,
|
|
3850
|
+
user1.publicKey,
|
|
3851
|
+
OrderType.LIMIT,
|
|
3852
|
+
MarketType.SPOT,
|
|
3853
|
+
2, // orderId
|
|
3854
|
+
marketIndex,
|
|
3855
|
+
new BN(12), // price
|
|
3856
|
+
BASE_PRECISION, // quantity
|
|
3857
|
+
PositionDirection.LONG,
|
|
3858
|
+
vBid,
|
|
3859
|
+
vAsk
|
|
3860
|
+
);
|
|
3861
|
+
insertOrderToDLOB(
|
|
3862
|
+
dlob,
|
|
3863
|
+
user2.publicKey,
|
|
3864
|
+
OrderType.LIMIT,
|
|
3865
|
+
MarketType.SPOT,
|
|
3866
|
+
3, // orderId
|
|
3867
|
+
marketIndex,
|
|
3868
|
+
new BN(13), // price
|
|
3869
|
+
BASE_PRECISION, // quantity
|
|
3870
|
+
PositionDirection.LONG,
|
|
3871
|
+
vBid,
|
|
3872
|
+
vAsk
|
|
3873
|
+
);
|
|
3874
|
+
|
|
3875
|
+
// should have no crossing orders
|
|
3876
|
+
const nodesToFillBefore = dlob.findCrossingNodesToFill(
|
|
3877
|
+
marketIndex,
|
|
3878
|
+
12, // auction over
|
|
3879
|
+
MarketType.SPOT,
|
|
3880
|
+
{
|
|
3881
|
+
price: vBid.add(vAsk).div(new BN(2)),
|
|
3882
|
+
slot: new BN(12),
|
|
3883
|
+
confidence: new BN(1),
|
|
3884
|
+
hasSufficientNumberOfDataPoints: true,
|
|
3885
|
+
}
|
|
3886
|
+
);
|
|
3887
|
+
expect(nodesToFillBefore.length).to.equal(0);
|
|
3888
|
+
|
|
3889
|
+
// place two market sell order eating 2 of the limit orders
|
|
3890
|
+
insertOrderToDLOB(
|
|
3891
|
+
dlob,
|
|
3892
|
+
user4.publicKey,
|
|
3893
|
+
OrderType.MARKET,
|
|
3894
|
+
MarketType.SPOT,
|
|
3895
|
+
4, // orderId
|
|
3896
|
+
marketIndex,
|
|
3897
|
+
new BN(12), // price
|
|
3898
|
+
new BN(1).mul(BASE_PRECISION), // quantity
|
|
3899
|
+
PositionDirection.SHORT,
|
|
3900
|
+
vBid,
|
|
3901
|
+
vAsk
|
|
3902
|
+
);
|
|
3903
|
+
insertOrderToDLOB(
|
|
3904
|
+
dlob,
|
|
3905
|
+
user5.publicKey,
|
|
3906
|
+
OrderType.MARKET,
|
|
3907
|
+
MarketType.SPOT,
|
|
3908
|
+
5, // orderId
|
|
3909
|
+
marketIndex,
|
|
3910
|
+
new BN(12), // price
|
|
3911
|
+
new BN(1).mul(BASE_PRECISION), // quantity
|
|
3912
|
+
PositionDirection.SHORT,
|
|
3913
|
+
vBid,
|
|
3914
|
+
vAsk
|
|
3915
|
+
);
|
|
3916
|
+
|
|
3917
|
+
const nodesToFillAfter = dlob.findCrossingNodesToFill(
|
|
3918
|
+
marketIndex,
|
|
3919
|
+
12, // auction over
|
|
3920
|
+
MarketType.SPOT,
|
|
3921
|
+
{
|
|
3922
|
+
price: vBid.add(vAsk).div(new BN(2)),
|
|
3923
|
+
slot: new BN(12),
|
|
3924
|
+
confidence: new BN(1),
|
|
3925
|
+
hasSufficientNumberOfDataPoints: true,
|
|
3926
|
+
}
|
|
3927
|
+
);
|
|
3928
|
+
for (const n of nodesToFillAfter) {
|
|
3929
|
+
console.log(
|
|
3930
|
+
`cross found: taker orderId: ${n.node.order?.orderId.toString()}: BAA: ${n.node.order?.baseAssetAmount.toString()}, maker orderId: ${n.makerNode?.order?.orderId.toString()}: BAA: ${n.makerNode?.order?.baseAssetAmount.toString()}`
|
|
3931
|
+
);
|
|
3932
|
+
}
|
|
3933
|
+
expect(nodesToFillAfter.length).to.equal(2);
|
|
3934
|
+
|
|
3935
|
+
// first taker should fill with best maker
|
|
3936
|
+
expect(nodesToFillAfter[0].node.order?.orderId).to.equal(4);
|
|
3937
|
+
expect(nodesToFillAfter[0].makerNode?.order?.orderId).to.equal(3);
|
|
3938
|
+
|
|
3939
|
+
// second taker should fill with second best maker
|
|
3940
|
+
expect(nodesToFillAfter[1].node.order?.orderId).to.equal(5);
|
|
3941
|
+
expect(nodesToFillAfter[1].makerNode?.order?.orderId).to.equal(2);
|
|
3942
|
+
});
|
|
3943
|
+
|
|
3944
|
+
it('Test one market order fills two limit orders', () => {
|
|
3945
|
+
const vAsk = new BN(15);
|
|
3946
|
+
const vBid = new BN(10);
|
|
3947
|
+
|
|
3948
|
+
const mockUserMap = new MockUserMap();
|
|
3949
|
+
const user0 = Keypair.generate();
|
|
3950
|
+
const userAuth0 = Keypair.generate();
|
|
3951
|
+
const user1 = Keypair.generate();
|
|
3952
|
+
const userAuth1 = Keypair.generate();
|
|
3953
|
+
const user2 = Keypair.generate();
|
|
3954
|
+
const userAuth2 = Keypair.generate();
|
|
3955
|
+
const user3 = Keypair.generate();
|
|
3956
|
+
const userAuth3 = Keypair.generate();
|
|
3957
|
+
|
|
3958
|
+
mockUserMap.addUserAccountAuthority(user0.publicKey, userAuth0.publicKey);
|
|
3959
|
+
mockUserMap.addUserAccountAuthority(user1.publicKey, userAuth1.publicKey);
|
|
3960
|
+
mockUserMap.addUserAccountAuthority(user2.publicKey, userAuth2.publicKey);
|
|
3961
|
+
mockUserMap.addUserAccountAuthority(user3.publicKey, userAuth3.publicKey);
|
|
3962
|
+
|
|
3963
|
+
const dlob = new DLOB(
|
|
3964
|
+
mockPerpMarkets,
|
|
3965
|
+
mockSpotMarkets,
|
|
3966
|
+
mockStateAccount,
|
|
3967
|
+
mockUserMap,
|
|
3968
|
+
false
|
|
3969
|
+
);
|
|
3970
|
+
const marketIndex = 0;
|
|
3971
|
+
|
|
3972
|
+
// insert some limit sells below vAMM ask, above bid
|
|
3973
|
+
insertOrderToDLOB(
|
|
3974
|
+
dlob,
|
|
3975
|
+
user0.publicKey,
|
|
3976
|
+
OrderType.LIMIT,
|
|
3977
|
+
MarketType.SPOT,
|
|
3978
|
+
1, // orderId
|
|
3979
|
+
marketIndex,
|
|
3980
|
+
new BN(14), // price
|
|
3981
|
+
BASE_PRECISION, // quantity
|
|
3982
|
+
PositionDirection.SHORT,
|
|
3983
|
+
vBid,
|
|
3984
|
+
vAsk
|
|
3985
|
+
);
|
|
3986
|
+
insertOrderToDLOB(
|
|
3987
|
+
dlob,
|
|
3988
|
+
user1.publicKey,
|
|
3989
|
+
OrderType.LIMIT,
|
|
3990
|
+
MarketType.SPOT,
|
|
3991
|
+
2, // orderId
|
|
3992
|
+
marketIndex,
|
|
3993
|
+
new BN(12), // price
|
|
3994
|
+
BASE_PRECISION, // quantity
|
|
3995
|
+
PositionDirection.SHORT,
|
|
3996
|
+
vBid,
|
|
3997
|
+
vAsk
|
|
3998
|
+
);
|
|
3999
|
+
insertOrderToDLOB(
|
|
4000
|
+
dlob,
|
|
4001
|
+
user2.publicKey,
|
|
4002
|
+
OrderType.LIMIT,
|
|
4003
|
+
MarketType.SPOT,
|
|
4004
|
+
3, // orderId
|
|
4005
|
+
marketIndex,
|
|
4006
|
+
new BN(11), // price
|
|
4007
|
+
BASE_PRECISION, // quantity
|
|
4008
|
+
PositionDirection.SHORT,
|
|
4009
|
+
vBid,
|
|
4010
|
+
vAsk
|
|
4011
|
+
);
|
|
4012
|
+
|
|
4013
|
+
// should have no crossing orders
|
|
4014
|
+
const nodesToFillBefore = dlob.findCrossingNodesToFill(
|
|
4015
|
+
marketIndex,
|
|
4016
|
+
12, // auction over
|
|
4017
|
+
MarketType.SPOT,
|
|
4018
|
+
{
|
|
4019
|
+
price: vBid.add(vAsk).div(new BN(2)),
|
|
4020
|
+
slot: new BN(12),
|
|
4021
|
+
confidence: new BN(1),
|
|
4022
|
+
hasSufficientNumberOfDataPoints: true,
|
|
4023
|
+
}
|
|
4024
|
+
);
|
|
4025
|
+
expect(nodesToFillBefore.length).to.equal(0);
|
|
4026
|
+
|
|
4027
|
+
// place one market buy order eating 2 of the limit orders
|
|
4028
|
+
insertOrderToDLOB(
|
|
4029
|
+
dlob,
|
|
4030
|
+
user3.publicKey,
|
|
4031
|
+
OrderType.MARKET,
|
|
4032
|
+
MarketType.SPOT,
|
|
4033
|
+
4, // orderId
|
|
4034
|
+
marketIndex,
|
|
4035
|
+
new BN(12), // price
|
|
4036
|
+
new BN(2).mul(BASE_PRECISION), // quantity
|
|
4037
|
+
PositionDirection.LONG,
|
|
4038
|
+
vBid,
|
|
4039
|
+
vAsk
|
|
4040
|
+
);
|
|
4041
|
+
|
|
4042
|
+
const nodesToFillAfter = dlob.findCrossingNodesToFill(
|
|
4043
|
+
marketIndex,
|
|
4044
|
+
12, // auction over
|
|
4045
|
+
MarketType.SPOT,
|
|
4046
|
+
{
|
|
4047
|
+
price: vBid.add(vAsk).div(new BN(2)),
|
|
4048
|
+
slot: new BN(12),
|
|
4049
|
+
confidence: new BN(1),
|
|
4050
|
+
hasSufficientNumberOfDataPoints: true,
|
|
4051
|
+
}
|
|
4052
|
+
);
|
|
4053
|
+
for (const n of nodesToFillAfter) {
|
|
4054
|
+
console.log(
|
|
4055
|
+
`cross found: taker orderId: ${n.node.order?.orderId.toString()}: BAA: ${n.node.order?.baseAssetAmountFilled.toString()}/${n.node.order?.baseAssetAmount.toString()}, maker orderId: ${n.makerNode?.order?.orderId.toString()}: BAA: ${n.makerNode?.order?.baseAssetAmountFilled.toString()}/${n.makerNode?.order?.baseAssetAmount.toString()}`
|
|
4056
|
+
);
|
|
4057
|
+
}
|
|
4058
|
+
expect(nodesToFillAfter.length).to.equal(2);
|
|
4059
|
+
|
|
4060
|
+
// taker should fill completely with best maker
|
|
4061
|
+
expect(nodesToFillAfter[0].node.order?.orderId).to.equal(4);
|
|
4062
|
+
expect(nodesToFillAfter[0].makerNode?.order?.orderId).to.equal(3);
|
|
4063
|
+
|
|
4064
|
+
// taker should fill completely with second best maker
|
|
4065
|
+
expect(nodesToFillAfter[1].node.order?.orderId).to.equal(4);
|
|
4066
|
+
expect(nodesToFillAfter[1].makerNode?.order?.orderId).to.equal(2);
|
|
4067
|
+
});
|
|
4068
|
+
|
|
4069
|
+
it('Test two market orders to fill one limit order', () => {
|
|
4070
|
+
const vAsk = new BN(15);
|
|
4071
|
+
const vBid = new BN(8);
|
|
4072
|
+
|
|
4073
|
+
const mockUserMap = new MockUserMap();
|
|
4074
|
+
const user0 = Keypair.generate();
|
|
4075
|
+
const userAuth0 = Keypair.generate();
|
|
4076
|
+
const user1 = Keypair.generate();
|
|
4077
|
+
const userAuth1 = Keypair.generate();
|
|
4078
|
+
const user2 = Keypair.generate();
|
|
4079
|
+
const userAuth2 = Keypair.generate();
|
|
4080
|
+
const user3 = Keypair.generate();
|
|
4081
|
+
const userAuth3 = Keypair.generate();
|
|
4082
|
+
const user4 = Keypair.generate();
|
|
4083
|
+
const userAuth4 = Keypair.generate();
|
|
4084
|
+
|
|
4085
|
+
mockUserMap.addUserAccountAuthority(user0.publicKey, userAuth0.publicKey);
|
|
4086
|
+
mockUserMap.addUserAccountAuthority(user1.publicKey, userAuth1.publicKey);
|
|
4087
|
+
mockUserMap.addUserAccountAuthority(user2.publicKey, userAuth2.publicKey);
|
|
4088
|
+
mockUserMap.addUserAccountAuthority(user3.publicKey, userAuth3.publicKey);
|
|
4089
|
+
mockUserMap.addUserAccountAuthority(user4.publicKey, userAuth4.publicKey);
|
|
4090
|
+
|
|
4091
|
+
const dlob = new DLOB(
|
|
4092
|
+
mockPerpMarkets,
|
|
4093
|
+
mockSpotMarkets,
|
|
4094
|
+
mockStateAccount,
|
|
4095
|
+
mockUserMap,
|
|
4096
|
+
false
|
|
4097
|
+
);
|
|
4098
|
+
const marketIndex = 0;
|
|
4099
|
+
|
|
4100
|
+
const slot = 12;
|
|
4101
|
+
const oracle = {
|
|
4102
|
+
price: vBid.add(vAsk).div(new BN(2)),
|
|
4103
|
+
slot: new BN(slot),
|
|
4104
|
+
confidence: new BN(1),
|
|
4105
|
+
hasSufficientNumberOfDataPoints: true,
|
|
4106
|
+
};
|
|
4107
|
+
|
|
4108
|
+
// insert some limit sells below vAMM ask, above bid
|
|
4109
|
+
insertOrderToDLOB(
|
|
4110
|
+
dlob,
|
|
4111
|
+
user0.publicKey,
|
|
4112
|
+
OrderType.LIMIT,
|
|
4113
|
+
MarketType.SPOT,
|
|
4114
|
+
1, // orderId
|
|
4115
|
+
marketIndex,
|
|
4116
|
+
new BN(14), // price
|
|
4117
|
+
BASE_PRECISION, // quantity
|
|
4118
|
+
PositionDirection.SHORT,
|
|
4119
|
+
vBid,
|
|
4120
|
+
vAsk
|
|
4121
|
+
);
|
|
4122
|
+
insertOrderToDLOB(
|
|
4123
|
+
dlob,
|
|
4124
|
+
user1.publicKey,
|
|
4125
|
+
OrderType.LIMIT,
|
|
4126
|
+
MarketType.SPOT,
|
|
4127
|
+
2, // orderId
|
|
4128
|
+
marketIndex,
|
|
4129
|
+
new BN(13), // price
|
|
4130
|
+
BASE_PRECISION, // quantity
|
|
4131
|
+
PositionDirection.SHORT,
|
|
4132
|
+
vBid,
|
|
4133
|
+
vAsk
|
|
4134
|
+
);
|
|
4135
|
+
insertOrderToDLOB(
|
|
4136
|
+
dlob,
|
|
4137
|
+
user2.publicKey,
|
|
4138
|
+
OrderType.LIMIT,
|
|
4139
|
+
MarketType.SPOT,
|
|
4140
|
+
3, // orderId
|
|
4141
|
+
marketIndex,
|
|
4142
|
+
new BN(8), // price <-- best price
|
|
4143
|
+
new BN(3).mul(BASE_PRECISION), // quantity
|
|
4144
|
+
PositionDirection.SHORT,
|
|
4145
|
+
vBid,
|
|
4146
|
+
vAsk
|
|
4147
|
+
);
|
|
4148
|
+
|
|
4149
|
+
// should have no crossing orders
|
|
4150
|
+
const nodesToFillBefore = dlob.findCrossingNodesToFill(
|
|
4151
|
+
marketIndex,
|
|
4152
|
+
12, // auction over
|
|
4153
|
+
MarketType.SPOT,
|
|
4154
|
+
oracle
|
|
4155
|
+
);
|
|
4156
|
+
expect(nodesToFillBefore.length).to.equal(0);
|
|
4157
|
+
|
|
4158
|
+
// place two market buy orders to eat the best ask
|
|
4159
|
+
insertOrderToDLOB(
|
|
4160
|
+
dlob,
|
|
4161
|
+
user3.publicKey,
|
|
4162
|
+
OrderType.MARKET,
|
|
4163
|
+
MarketType.SPOT,
|
|
4164
|
+
4, // orderId
|
|
4165
|
+
marketIndex,
|
|
4166
|
+
new BN(0), // price
|
|
4167
|
+
new BN(1).mul(BASE_PRECISION), // quantity
|
|
4168
|
+
PositionDirection.LONG,
|
|
4169
|
+
vBid,
|
|
4170
|
+
vAsk
|
|
4171
|
+
);
|
|
4172
|
+
insertOrderToDLOB(
|
|
4173
|
+
dlob,
|
|
4174
|
+
user4.publicKey,
|
|
4175
|
+
OrderType.MARKET,
|
|
4176
|
+
MarketType.SPOT,
|
|
4177
|
+
5, // orderId
|
|
4178
|
+
marketIndex,
|
|
4179
|
+
new BN(0), // price
|
|
4180
|
+
new BN(2).mul(BASE_PRECISION), // quantity
|
|
4181
|
+
PositionDirection.LONG,
|
|
4182
|
+
vBid,
|
|
4183
|
+
vAsk
|
|
4184
|
+
);
|
|
4185
|
+
|
|
4186
|
+
const nodesToFillAfter = dlob.findCrossingNodesToFill(
|
|
4187
|
+
marketIndex,
|
|
4188
|
+
slot, // auction over
|
|
4189
|
+
MarketType.SPOT,
|
|
4190
|
+
oracle
|
|
4191
|
+
);
|
|
4192
|
+
const mktNodes = dlob.findExpiredNodesToFill(
|
|
4193
|
+
marketIndex,
|
|
4194
|
+
slot,
|
|
4195
|
+
MarketType.SPOT
|
|
4196
|
+
);
|
|
4197
|
+
console.log(`market nodes: ${mktNodes.length}`);
|
|
4198
|
+
|
|
4199
|
+
printBookState(dlob, marketIndex, vBid, vAsk, slot, oracle);
|
|
4200
|
+
|
|
4201
|
+
for (const n of nodesToFillAfter) {
|
|
4202
|
+
console.log(
|
|
4203
|
+
`cross found: taker orderId: ${n.node.order?.orderId.toString()}: BAA: ${n.node.order?.baseAssetAmountFilled.toString()}/${n.node.order?.baseAssetAmount.toString()}, maker orderId: ${n.makerNode?.order?.orderId.toString()}: BAA: ${n.makerNode?.order?.baseAssetAmountFilled.toString()}/${n.makerNode?.order?.baseAssetAmount.toString()}`
|
|
4204
|
+
);
|
|
4205
|
+
}
|
|
4206
|
+
expect(nodesToFillAfter.length).to.equal(2);
|
|
4207
|
+
|
|
4208
|
+
// taker should fill completely with best maker
|
|
4209
|
+
expect(nodesToFillAfter[0].node.order?.orderId).to.equal(4);
|
|
4210
|
+
expect(nodesToFillAfter[0].makerNode?.order?.orderId).to.equal(3);
|
|
4211
|
+
|
|
4212
|
+
// taker should fill completely with second best maker
|
|
4213
|
+
expect(nodesToFillAfter[1].node.order?.orderId).to.equal(5);
|
|
4214
|
+
expect(nodesToFillAfter[1].makerNode?.order?.orderId).to.equal(3);
|
|
4215
|
+
});
|
|
4216
|
+
|
|
4217
|
+
it('Test trigger orders', () => {
|
|
4218
|
+
const vAsk = new BN(15);
|
|
4219
|
+
const vBid = new BN(8);
|
|
4220
|
+
|
|
4221
|
+
const mockUserMap = new MockUserMap();
|
|
4222
|
+
const user0 = Keypair.generate();
|
|
4223
|
+
const userAuth0 = Keypair.generate();
|
|
4224
|
+
const user1 = Keypair.generate();
|
|
4225
|
+
const userAuth1 = Keypair.generate();
|
|
4226
|
+
const user2 = Keypair.generate();
|
|
4227
|
+
const userAuth2 = Keypair.generate();
|
|
4228
|
+
const user3 = Keypair.generate();
|
|
4229
|
+
const userAuth3 = Keypair.generate();
|
|
4230
|
+
const user4 = Keypair.generate();
|
|
4231
|
+
const userAuth4 = Keypair.generate();
|
|
4232
|
+
const user5 = Keypair.generate();
|
|
4233
|
+
const userAuth5 = Keypair.generate();
|
|
4234
|
+
const user6 = Keypair.generate();
|
|
4235
|
+
const userAuth6 = Keypair.generate();
|
|
4236
|
+
const user7 = Keypair.generate();
|
|
4237
|
+
const userAuth7 = Keypair.generate();
|
|
4238
|
+
const user8 = Keypair.generate();
|
|
4239
|
+
const userAuth8 = Keypair.generate();
|
|
4240
|
+
const user9 = Keypair.generate();
|
|
4241
|
+
const userAuth9 = Keypair.generate();
|
|
4242
|
+
const user10 = Keypair.generate();
|
|
4243
|
+
const userAuth10 = Keypair.generate();
|
|
4244
|
+
const user11 = Keypair.generate();
|
|
4245
|
+
const userAuth11 = Keypair.generate();
|
|
4246
|
+
|
|
4247
|
+
mockUserMap.addUserAccountAuthority(user0.publicKey, userAuth0.publicKey);
|
|
4248
|
+
mockUserMap.addUserAccountAuthority(user1.publicKey, userAuth1.publicKey);
|
|
4249
|
+
mockUserMap.addUserAccountAuthority(user2.publicKey, userAuth2.publicKey);
|
|
4250
|
+
mockUserMap.addUserAccountAuthority(user3.publicKey, userAuth3.publicKey);
|
|
4251
|
+
mockUserMap.addUserAccountAuthority(user4.publicKey, userAuth4.publicKey);
|
|
4252
|
+
mockUserMap.addUserAccountAuthority(user5.publicKey, userAuth5.publicKey);
|
|
4253
|
+
mockUserMap.addUserAccountAuthority(user6.publicKey, userAuth6.publicKey);
|
|
4254
|
+
mockUserMap.addUserAccountAuthority(user7.publicKey, userAuth7.publicKey);
|
|
4255
|
+
mockUserMap.addUserAccountAuthority(user8.publicKey, userAuth8.publicKey);
|
|
4256
|
+
mockUserMap.addUserAccountAuthority(user9.publicKey, userAuth9.publicKey);
|
|
4257
|
+
mockUserMap.addUserAccountAuthority(user10.publicKey, userAuth10.publicKey);
|
|
4258
|
+
mockUserMap.addUserAccountAuthority(user11.publicKey, userAuth11.publicKey);
|
|
4259
|
+
|
|
4260
|
+
const dlob = new DLOB(
|
|
4261
|
+
mockPerpMarkets,
|
|
4262
|
+
mockSpotMarkets,
|
|
4263
|
+
mockStateAccount,
|
|
4264
|
+
mockUserMap,
|
|
4265
|
+
false
|
|
4266
|
+
);
|
|
4267
|
+
const marketIndex = 0;
|
|
4268
|
+
|
|
4269
|
+
const slot = 20;
|
|
4270
|
+
const oracle = {
|
|
4271
|
+
price: vBid.add(vAsk).div(new BN(2)),
|
|
4272
|
+
slot: new BN(slot),
|
|
4273
|
+
confidence: new BN(1),
|
|
4274
|
+
hasSufficientNumberOfDataPoints: true,
|
|
4275
|
+
};
|
|
4276
|
+
|
|
4277
|
+
const orderIdsToTrigger = [1, 2, 3, 4, 5, 6, 7, 8];
|
|
4278
|
+
// const orderIdsToNotTrigger = [9, 10, 11, 12];
|
|
4279
|
+
|
|
4280
|
+
// should trigger limit buy with above condition
|
|
4281
|
+
insertTriggerOrderToDLOB(
|
|
4282
|
+
dlob,
|
|
4283
|
+
user0.publicKey,
|
|
4284
|
+
OrderType.TRIGGER_LIMIT,
|
|
4285
|
+
MarketType.SPOT,
|
|
4286
|
+
1, //orderId
|
|
4287
|
+
marketIndex, // marketIndex
|
|
4288
|
+
vBid, // price
|
|
4289
|
+
BASE_PRECISION, // baseAssetAmount: BN,
|
|
4290
|
+
PositionDirection.LONG,
|
|
4291
|
+
oracle.price.sub(new BN(1)), // triggerPrice: BN,
|
|
4292
|
+
OrderTriggerCondition.ABOVE, // triggerCondition: OrderTriggerCondition,
|
|
4293
|
+
vBid,
|
|
4294
|
+
vAsk
|
|
4295
|
+
);
|
|
4296
|
+
// should trigger limit sell with above condition
|
|
4297
|
+
insertTriggerOrderToDLOB(
|
|
4298
|
+
dlob,
|
|
4299
|
+
user1.publicKey,
|
|
4300
|
+
OrderType.TRIGGER_LIMIT,
|
|
4301
|
+
MarketType.SPOT,
|
|
4302
|
+
2, //orderId
|
|
4303
|
+
marketIndex, // marketIndex
|
|
4304
|
+
vBid, // price
|
|
4305
|
+
BASE_PRECISION, // baseAssetAmount: BN,
|
|
4306
|
+
PositionDirection.SHORT,
|
|
4307
|
+
oracle.price.sub(new BN(1)), // triggerPrice: BN,
|
|
4308
|
+
OrderTriggerCondition.ABOVE, // triggerCondition: OrderTriggerCondition,
|
|
4309
|
+
vBid,
|
|
4310
|
+
vAsk
|
|
4311
|
+
);
|
|
4312
|
+
// should trigger market buy with above condition
|
|
4313
|
+
insertTriggerOrderToDLOB(
|
|
4314
|
+
dlob,
|
|
4315
|
+
user2.publicKey,
|
|
4316
|
+
OrderType.TRIGGER_MARKET,
|
|
4317
|
+
MarketType.SPOT,
|
|
4318
|
+
3, //orderId
|
|
4319
|
+
marketIndex, // marketIndex
|
|
4320
|
+
vAsk, // price
|
|
4321
|
+
BASE_PRECISION, // baseAssetAmount: BN,
|
|
4322
|
+
PositionDirection.LONG,
|
|
4323
|
+
oracle.price.sub(new BN(1)), // triggerPrice: BN,
|
|
4324
|
+
OrderTriggerCondition.ABOVE, // triggerCondition: OrderTriggerCondition,
|
|
4325
|
+
vBid,
|
|
4326
|
+
vAsk
|
|
4327
|
+
);
|
|
4328
|
+
// should trigger market sell with above condition
|
|
4329
|
+
insertTriggerOrderToDLOB(
|
|
4330
|
+
dlob,
|
|
4331
|
+
user3.publicKey,
|
|
4332
|
+
OrderType.TRIGGER_MARKET,
|
|
4333
|
+
MarketType.SPOT,
|
|
4334
|
+
4, //orderId
|
|
4335
|
+
marketIndex, // marketIndex
|
|
4336
|
+
vBid, // price
|
|
4337
|
+
BASE_PRECISION, // baseAssetAmount: BN,
|
|
4338
|
+
PositionDirection.SHORT,
|
|
4339
|
+
oracle.price.sub(new BN(1)), // triggerPrice: BN,
|
|
4340
|
+
OrderTriggerCondition.ABOVE, // triggerCondition: OrderTriggerCondition,
|
|
4341
|
+
vBid,
|
|
4342
|
+
vAsk
|
|
4343
|
+
);
|
|
4344
|
+
// should trigger limit buy with below condition
|
|
4345
|
+
insertTriggerOrderToDLOB(
|
|
4346
|
+
dlob,
|
|
4347
|
+
user4.publicKey,
|
|
4348
|
+
OrderType.TRIGGER_LIMIT,
|
|
4349
|
+
MarketType.SPOT,
|
|
4350
|
+
5, //orderId
|
|
4351
|
+
marketIndex, // marketIndex
|
|
4352
|
+
vBid, // price
|
|
4353
|
+
BASE_PRECISION, // baseAssetAmount: BN,
|
|
4354
|
+
PositionDirection.LONG,
|
|
4355
|
+
oracle.price.add(new BN(1)), // triggerPrice: BN,
|
|
4356
|
+
OrderTriggerCondition.BELOW, // triggerCondition: OrderTriggerCondition,
|
|
4357
|
+
vBid,
|
|
4358
|
+
vAsk
|
|
4359
|
+
);
|
|
4360
|
+
// should trigger limit sell with below condition
|
|
4361
|
+
insertTriggerOrderToDLOB(
|
|
4362
|
+
dlob,
|
|
4363
|
+
user5.publicKey,
|
|
4364
|
+
OrderType.TRIGGER_LIMIT,
|
|
4365
|
+
MarketType.SPOT,
|
|
4366
|
+
6, //orderId
|
|
4367
|
+
marketIndex, // marketIndex
|
|
4368
|
+
vBid, // price
|
|
4369
|
+
BASE_PRECISION, // baseAssetAmount: BN,
|
|
4370
|
+
PositionDirection.SHORT,
|
|
4371
|
+
oracle.price.add(new BN(1)), // triggerPrice: BN,
|
|
4372
|
+
OrderTriggerCondition.BELOW, // triggerCondition: OrderTriggerCondition,
|
|
4373
|
+
vBid,
|
|
4374
|
+
vAsk
|
|
4375
|
+
);
|
|
4376
|
+
// should trigger market buy with below condition
|
|
4377
|
+
insertTriggerOrderToDLOB(
|
|
4378
|
+
dlob,
|
|
4379
|
+
user6.publicKey,
|
|
4380
|
+
OrderType.TRIGGER_MARKET,
|
|
4381
|
+
MarketType.SPOT,
|
|
4382
|
+
7, //orderId
|
|
4383
|
+
marketIndex, // marketIndex
|
|
4384
|
+
vBid, // price
|
|
4385
|
+
BASE_PRECISION, // baseAssetAmount: BN,
|
|
4386
|
+
PositionDirection.LONG,
|
|
4387
|
+
oracle.price.add(new BN(1)), // triggerPrice: BN,
|
|
4388
|
+
OrderTriggerCondition.BELOW, // triggerCondition: OrderTriggerCondition,
|
|
4389
|
+
vBid,
|
|
4390
|
+
vAsk
|
|
4391
|
+
);
|
|
4392
|
+
// should trigger market sell with below condition
|
|
4393
|
+
insertTriggerOrderToDLOB(
|
|
4394
|
+
dlob,
|
|
4395
|
+
user7.publicKey,
|
|
4396
|
+
OrderType.TRIGGER_MARKET,
|
|
4397
|
+
MarketType.SPOT,
|
|
4398
|
+
8, //orderId
|
|
4399
|
+
marketIndex, // marketIndex
|
|
4400
|
+
vBid, // price
|
|
4401
|
+
BASE_PRECISION, // baseAssetAmount: BN,
|
|
4402
|
+
PositionDirection.LONG,
|
|
4403
|
+
oracle.price.add(new BN(1)), // triggerPrice: BN,
|
|
4404
|
+
OrderTriggerCondition.BELOW, // triggerCondition: OrderTriggerCondition,
|
|
4405
|
+
vBid,
|
|
4406
|
+
vAsk
|
|
4407
|
+
);
|
|
4408
|
+
|
|
4409
|
+
// should NOT trigger market sell with above condition
|
|
4410
|
+
insertTriggerOrderToDLOB(
|
|
4411
|
+
dlob,
|
|
4412
|
+
user8.publicKey,
|
|
4413
|
+
OrderType.TRIGGER_MARKET,
|
|
4414
|
+
MarketType.SPOT,
|
|
4415
|
+
9, //orderId
|
|
4416
|
+
marketIndex, // marketIndex
|
|
4417
|
+
vBid, // price
|
|
4418
|
+
BASE_PRECISION, // baseAssetAmount: BN,
|
|
4419
|
+
PositionDirection.SHORT,
|
|
4420
|
+
oracle.price.add(new BN(1)), // triggerPrice: BN,
|
|
4421
|
+
OrderTriggerCondition.ABOVE, // triggerCondition: OrderTriggerCondition,
|
|
4422
|
+
vBid,
|
|
4423
|
+
vAsk
|
|
4424
|
+
);
|
|
4425
|
+
// should NOT trigger market sell with below condition
|
|
4426
|
+
insertTriggerOrderToDLOB(
|
|
4427
|
+
dlob,
|
|
4428
|
+
user9.publicKey,
|
|
4429
|
+
OrderType.TRIGGER_MARKET,
|
|
4430
|
+
MarketType.SPOT,
|
|
4431
|
+
10, //orderId
|
|
4432
|
+
marketIndex, // marketIndex
|
|
4433
|
+
vBid, // price
|
|
4434
|
+
BASE_PRECISION, // baseAssetAmount: BN,
|
|
4435
|
+
PositionDirection.SHORT,
|
|
4436
|
+
oracle.price.sub(new BN(1)), // triggerPrice: BN,
|
|
4437
|
+
OrderTriggerCondition.BELOW, // triggerCondition: OrderTriggerCondition,
|
|
4438
|
+
vBid,
|
|
4439
|
+
vAsk
|
|
4440
|
+
);
|
|
4441
|
+
// should NOT trigger market buy with above condition
|
|
4442
|
+
insertTriggerOrderToDLOB(
|
|
4443
|
+
dlob,
|
|
4444
|
+
user10.publicKey,
|
|
4445
|
+
OrderType.TRIGGER_MARKET,
|
|
4446
|
+
MarketType.SPOT,
|
|
4447
|
+
11, //orderId
|
|
4448
|
+
marketIndex, // marketIndex
|
|
4449
|
+
vBid, // price
|
|
4450
|
+
BASE_PRECISION, // baseAssetAmount: BN,
|
|
4451
|
+
PositionDirection.LONG,
|
|
4452
|
+
oracle.price.add(new BN(1)), // triggerPrice: BN,
|
|
4453
|
+
OrderTriggerCondition.ABOVE, // triggerCondition: OrderTriggerCondition,
|
|
4454
|
+
vBid,
|
|
4455
|
+
vAsk
|
|
4456
|
+
);
|
|
4457
|
+
// should NOT trigger market buy with below condition
|
|
4458
|
+
insertTriggerOrderToDLOB(
|
|
4459
|
+
dlob,
|
|
4460
|
+
user11.publicKey,
|
|
4461
|
+
OrderType.TRIGGER_MARKET,
|
|
4462
|
+
MarketType.SPOT,
|
|
4463
|
+
12, //orderId
|
|
4464
|
+
marketIndex, // marketIndex
|
|
4465
|
+
vBid, // price
|
|
4466
|
+
BASE_PRECISION, // baseAssetAmount: BN,
|
|
4467
|
+
PositionDirection.LONG,
|
|
4468
|
+
oracle.price.sub(new BN(1)), // triggerPrice: BN,
|
|
4469
|
+
OrderTriggerCondition.BELOW, // triggerCondition: OrderTriggerCondition,
|
|
4470
|
+
vBid,
|
|
4471
|
+
vAsk
|
|
4472
|
+
);
|
|
4473
|
+
|
|
4474
|
+
const nodesToTrigger = dlob.findNodesToTrigger(
|
|
4475
|
+
marketIndex,
|
|
4476
|
+
slot,
|
|
4477
|
+
oracle.price,
|
|
4478
|
+
MarketType.SPOT
|
|
4479
|
+
);
|
|
4480
|
+
console.log(`nodesToTriggeR: ${nodesToTrigger.length}`);
|
|
4481
|
+
for (const [idx, n] of nodesToTrigger.entries()) {
|
|
4482
|
+
expect(n.node.order?.orderId).to.equal(orderIdsToTrigger[idx]);
|
|
4483
|
+
console.log(`nodeToTrigger: ${n.node.order?.orderId}`);
|
|
4484
|
+
}
|
|
4485
|
+
});
|
|
4486
|
+
|
|
4487
|
+
it('Test will return expired market orders to fill', () => {
|
|
4488
|
+
const vAsk = new BN(15);
|
|
4489
|
+
const vBid = new BN(8);
|
|
4490
|
+
|
|
4491
|
+
const mockUserMap = new MockUserMap();
|
|
4492
|
+
const user0 = Keypair.generate();
|
|
4493
|
+
const userAuth0 = Keypair.generate();
|
|
4494
|
+
const user1 = Keypair.generate();
|
|
4495
|
+
const userAuth1 = Keypair.generate();
|
|
4496
|
+
mockUserMap.addUserAccountAuthority(user0.publicKey, userAuth0.publicKey);
|
|
4497
|
+
mockUserMap.addUserAccountAuthority(user1.publicKey, userAuth1.publicKey);
|
|
4498
|
+
|
|
4499
|
+
const dlob = new DLOB(
|
|
4500
|
+
mockPerpMarkets,
|
|
4501
|
+
mockSpotMarkets,
|
|
4502
|
+
mockStateAccount,
|
|
4503
|
+
mockUserMap,
|
|
4504
|
+
false
|
|
4505
|
+
);
|
|
4506
|
+
const marketIndex = 0;
|
|
4507
|
+
|
|
4508
|
+
const slot = 20;
|
|
4509
|
+
const ts = 20;
|
|
4510
|
+
const maxTs = new BN(30);
|
|
4511
|
+
|
|
4512
|
+
// non crossing bid
|
|
4513
|
+
insertOrderToDLOB(
|
|
4514
|
+
dlob,
|
|
4515
|
+
user0.publicKey,
|
|
4516
|
+
OrderType.MARKET,
|
|
4517
|
+
MarketType.SPOT,
|
|
4518
|
+
255, // orderId
|
|
4519
|
+
marketIndex,
|
|
4520
|
+
new BN(2), // price, very low, don't cross vamm
|
|
4521
|
+
BASE_PRECISION, // quantity
|
|
4522
|
+
PositionDirection.LONG,
|
|
4523
|
+
vBid,
|
|
4524
|
+
vAsk,
|
|
4525
|
+
new BN(slot),
|
|
4526
|
+
maxTs
|
|
4527
|
+
);
|
|
4528
|
+
insertOrderToDLOB(
|
|
4529
|
+
dlob,
|
|
4530
|
+
user1.publicKey,
|
|
4531
|
+
OrderType.MARKET,
|
|
4532
|
+
MarketType.SPOT,
|
|
4533
|
+
2, // orderId
|
|
4534
|
+
marketIndex,
|
|
4535
|
+
new BN(30), // price, very high, don't cross vamm
|
|
4536
|
+
BASE_PRECISION, // quantity
|
|
4537
|
+
PositionDirection.SHORT,
|
|
4538
|
+
vAsk,
|
|
4539
|
+
vBid,
|
|
4540
|
+
new BN(slot),
|
|
4541
|
+
maxTs
|
|
4542
|
+
);
|
|
4543
|
+
|
|
4544
|
+
// order auction is not yet complete, and order is not expired.
|
|
4545
|
+
const slot0 = slot;
|
|
4546
|
+
const ts0 = ts;
|
|
4547
|
+
const nodesToFillBefore = dlob.findNodesToFill(
|
|
4548
|
+
marketIndex,
|
|
4549
|
+
vBid,
|
|
4550
|
+
vAsk,
|
|
4551
|
+
slot0,
|
|
4552
|
+
ts0,
|
|
4553
|
+
MarketType.SPOT,
|
|
4554
|
+
{
|
|
4555
|
+
price: vBid.add(vAsk).div(new BN(2)),
|
|
4556
|
+
slot: new BN(slot0),
|
|
4557
|
+
confidence: new BN(1),
|
|
4558
|
+
hasSufficientNumberOfDataPoints: true,
|
|
4559
|
+
}
|
|
4560
|
+
);
|
|
4561
|
+
expect(nodesToFillBefore.length).to.equal(0);
|
|
4562
|
+
|
|
4563
|
+
// should get order to fill after timeInForce
|
|
4564
|
+
const slot1 = slot0 + slot * 2; // overshoots expiry
|
|
4565
|
+
const ts1 = ts + ts * 2;
|
|
4566
|
+
const nodesToFillAfter = dlob.findNodesToFill(
|
|
4567
|
+
marketIndex,
|
|
4568
|
+
vBid,
|
|
4569
|
+
vAsk,
|
|
4570
|
+
slot1,
|
|
4571
|
+
ts1,
|
|
4572
|
+
MarketType.SPOT,
|
|
4573
|
+
{
|
|
4574
|
+
price: vBid.add(vAsk).div(new BN(2)),
|
|
4575
|
+
slot: new BN(slot1),
|
|
4576
|
+
confidence: new BN(1),
|
|
4577
|
+
hasSufficientNumberOfDataPoints: true,
|
|
4578
|
+
}
|
|
4579
|
+
);
|
|
4580
|
+
expect(nodesToFillAfter.length).to.equal(2);
|
|
4581
|
+
|
|
4582
|
+
// check that the nodes have no makers
|
|
4583
|
+
expect(nodesToFillAfter[0].makerNode).to.equal(undefined);
|
|
4584
|
+
expect(nodesToFillAfter[1].makerNode).to.equal(undefined);
|
|
4585
|
+
});
|
|
4586
|
+
});
|