@drift-labs/sdk 0.2.0-master.26 → 0.2.0-master.27
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/accounts/pollingClearingHouseAccountSubscriber.d.ts +1 -0
- package/lib/accounts/pollingClearingHouseAccountSubscriber.js +3 -0
- package/lib/accounts/types.d.ts +1 -0
- package/lib/accounts/webSocketClearingHouseAccountSubscriber.d.ts +1 -0
- package/lib/accounts/webSocketClearingHouseAccountSubscriber.js +3 -0
- package/lib/admin.d.ts +5 -2
- package/lib/admin.js +30 -4
- package/lib/clearingHouse.d.ts +2 -0
- package/lib/clearingHouse.js +14 -1
- package/lib/clearingHouseUser.d.ts +11 -9
- package/lib/clearingHouseUser.js +182 -106
- package/lib/config.js +1 -1
- package/lib/dlob/DLOB.d.ts +73 -0
- package/lib/dlob/DLOB.js +557 -0
- package/lib/dlob/DLOBNode.d.ts +52 -0
- package/lib/dlob/DLOBNode.js +82 -0
- package/lib/dlob/NodeList.d.ts +26 -0
- package/lib/dlob/NodeList.js +138 -0
- package/lib/idl/clearing_house.json +122 -202
- package/lib/index.d.ts +5 -0
- package/lib/index.js +5 -0
- package/lib/math/market.d.ts +1 -1
- package/lib/math/market.js +1 -1
- package/lib/math/orders.d.ts +3 -3
- package/lib/math/orders.js +31 -16
- package/lib/math/spotBalance.d.ts +3 -0
- package/lib/math/spotBalance.js +18 -1
- package/lib/math/spotPosition.d.ts +5 -1
- package/lib/math/spotPosition.js +16 -1
- package/lib/types.d.ts +24 -23
- package/lib/types.js +9 -3
- package/lib/userMap/userMap.d.ts +25 -0
- package/lib/userMap/userMap.js +73 -0
- package/lib/userMap/userStatsMap.d.ts +19 -0
- package/lib/userMap/userStatsMap.js +68 -0
- package/package.json +6 -3
- package/src/accounts/pollingClearingHouseAccountSubscriber.ts +4 -0
- package/src/accounts/types.ts +1 -0
- package/src/accounts/webSocketClearingHouseAccountSubscriber.ts +6 -0
- package/src/addresses/marketAddresses.js +26 -0
- package/src/admin.ts +50 -8
- package/src/assert/assert.js +1 -1
- package/src/clearingHouse.ts +23 -1
- package/src/clearingHouseUser.ts +364 -165
- package/src/config.ts +1 -1
- package/src/constants/banks.js +42 -0
- package/src/constants/{perpMarkets.js → markets.js} +11 -11
- package/src/dlob/DLOB.ts +884 -0
- package/src/dlob/DLOBNode.ts +163 -0
- package/src/dlob/NodeList.ts +185 -0
- package/src/events/eventList.js +23 -66
- package/src/events/txEventCache.js +19 -22
- package/src/examples/makeTradeExample.js +157 -0
- package/src/factory/bigNum.js +180 -183
- package/src/factory/oracleClient.js +9 -9
- package/src/idl/clearing_house.json +122 -202
- package/src/index.ts +5 -0
- package/src/math/auction.js +10 -10
- package/src/math/conversion.js +3 -4
- package/src/math/funding.js +175 -223
- package/src/math/market.ts +1 -1
- package/src/math/orders.ts +29 -21
- package/src/math/repeg.js +40 -40
- package/src/math/spotBalance.ts +26 -0
- package/src/math/spotPosition.ts +42 -1
- package/src/math/trade.js +74 -81
- package/src/math/utils.js +7 -8
- package/src/math/utils.js.map +1 -0
- package/src/oracles/oracleClientCache.js +9 -10
- package/src/oracles/pythClient.js +17 -52
- package/src/oracles/quoteAssetOracleClient.js +13 -44
- package/src/oracles/switchboardClient.js +37 -69
- package/src/oracles/types.js +1 -1
- package/src/token/index.js +4 -4
- package/src/tx/types.js +1 -1
- package/src/tx/utils.js +6 -7
- package/src/types.ts +25 -23
- package/src/userMap/userMap.ts +100 -0
- package/src/userMap/userStatsMap.ts +110 -0
- package/src/userName.js +5 -5
- package/src/util/computeUnits.js +11 -46
- package/src/util/getTokenAddress.js +9 -0
- package/src/util/promiseTimeout.js +5 -5
- package/src/util/tps.js +12 -46
- package/src/wallet.js +18 -55
- package/tests/bn/test.ts +2 -3
- package/tests/dlob/helpers.ts +322 -0
- package/tests/dlob/test.ts +2865 -0
- package/my-script/.env +0 -7
- package/my-script/getUserStats.ts +0 -106
- package/my-script/multiConnections.ts +0 -119
- package/my-script/test-regex.ts +0 -11
- package/my-script/utils.ts +0 -52
- package/src/accounts/bulkAccountLoader.js +0 -249
- package/src/accounts/bulkUserStatsSubscription.js +0 -75
- package/src/accounts/bulkUserSubscription.js +0 -75
- package/src/accounts/fetch.js +0 -92
- package/src/accounts/pollingClearingHouseAccountSubscriber.js +0 -465
- package/src/accounts/pollingOracleSubscriber.js +0 -156
- package/src/accounts/pollingTokenAccountSubscriber.js +0 -141
- package/src/accounts/pollingUserAccountSubscriber.js +0 -208
- package/src/accounts/pollingUserStatsAccountSubscriber.js +0 -208
- package/src/accounts/types.js +0 -28
- package/src/accounts/utils.js +0 -7
- package/src/accounts/webSocketAccountSubscriber.js +0 -138
- package/src/accounts/webSocketClearingHouseAccountSubscriber.js +0 -433
- package/src/accounts/webSocketUserAccountSubscriber.js +0 -113
- package/src/accounts/webSocketUserStatsAccountSubsriber.js +0 -113
- package/src/addresses/pda.js +0 -186
- package/src/admin.js +0 -1284
- package/src/clearingHouse.js +0 -3433
- package/src/clearingHouseConfig.js +0 -2
- package/src/clearingHouseUser.js +0 -874
- package/src/clearingHouseUserConfig.js +0 -2
- package/src/clearingHouseUserStats.js +0 -115
- package/src/clearingHouseUserStatsConfig.js +0 -2
- package/src/config.js +0 -80
- package/src/constants/numericConstants.js +0 -48
- package/src/constants/spotMarkets.js +0 -51
- package/src/events/eventSubscriber.js +0 -202
- package/src/events/fetchLogs.js +0 -117
- package/src/events/pollingLogProvider.js +0 -113
- package/src/events/sort.js +0 -41
- package/src/events/types.js +0 -25
- package/src/events/webSocketLogProvider.js +0 -76
- package/src/index.js +0 -75
- package/src/math/amm.js +0 -422
- package/src/math/insurance.js +0 -27
- package/src/math/margin.js +0 -77
- package/src/math/market.js +0 -105
- package/src/math/oracles.js +0 -56
- package/src/math/orders.js +0 -153
- package/src/math/position.js +0 -172
- package/src/math/spotBalance.js +0 -176
- package/src/math/spotMarket.js +0 -8
- package/src/math/spotPosition.js +0 -8
- package/src/orderParams.js +0 -28
- package/src/serum/serumSubscriber.js +0 -102
- package/src/serum/types.js +0 -2
- package/src/slot/SlotSubscriber.js +0 -86
- package/src/tokenFaucet.js +0 -323
- package/src/tx/retryTxSender.js +0 -280
- package/src/types.js +0 -216
package/src/dlob/DLOB.ts
ADDED
|
@@ -0,0 +1,884 @@
|
|
|
1
|
+
import { getOrderSignature, getVammNodeGenerator, NodeList } from './NodeList';
|
|
2
|
+
import {
|
|
3
|
+
MarketType,
|
|
4
|
+
BN,
|
|
5
|
+
calculateAskPrice,
|
|
6
|
+
calculateBidPrice,
|
|
7
|
+
ClearingHouse,
|
|
8
|
+
convertToNumber,
|
|
9
|
+
isAuctionComplete,
|
|
10
|
+
isOrderExpired,
|
|
11
|
+
isOneOfVariant,
|
|
12
|
+
isVariant,
|
|
13
|
+
getVariant,
|
|
14
|
+
Order,
|
|
15
|
+
ZERO,
|
|
16
|
+
MARK_PRICE_PRECISION,
|
|
17
|
+
SpotMarketAccount,
|
|
18
|
+
PerpMarketAccount,
|
|
19
|
+
OraclePriceData,
|
|
20
|
+
SlotSubscriber,
|
|
21
|
+
UserMap,
|
|
22
|
+
MarketTypeStr,
|
|
23
|
+
} from '..';
|
|
24
|
+
import { PublicKey } from '@solana/web3.js';
|
|
25
|
+
import { DLOBNode, DLOBNodeType, TriggerOrderNode } from '..';
|
|
26
|
+
|
|
27
|
+
export type MarketNodeLists = {
|
|
28
|
+
limit: {
|
|
29
|
+
ask: NodeList<'limit'>;
|
|
30
|
+
bid: NodeList<'limit'>;
|
|
31
|
+
};
|
|
32
|
+
floatingLimit: {
|
|
33
|
+
ask: NodeList<'floatingLimit'>;
|
|
34
|
+
bid: NodeList<'floatingLimit'>;
|
|
35
|
+
};
|
|
36
|
+
market: {
|
|
37
|
+
ask: NodeList<'market'>;
|
|
38
|
+
bid: NodeList<'market'>;
|
|
39
|
+
};
|
|
40
|
+
trigger: {
|
|
41
|
+
above: NodeList<'trigger'>;
|
|
42
|
+
below: NodeList<'trigger'>;
|
|
43
|
+
};
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
type OrderBookCallback = () => void;
|
|
47
|
+
|
|
48
|
+
export type NodeToFill = {
|
|
49
|
+
node: DLOBNode;
|
|
50
|
+
makerNode?: DLOBNode;
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
export type NodeToTrigger = {
|
|
54
|
+
node: TriggerOrderNode;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
type Side = 'ask' | 'bid' | 'both';
|
|
58
|
+
|
|
59
|
+
export class DLOB {
|
|
60
|
+
openOrders = new Map<MarketTypeStr, Set<string>>();
|
|
61
|
+
orderLists = new Map<MarketTypeStr, Map<number, MarketNodeLists>>();
|
|
62
|
+
marketIndexToAccount = new Map<
|
|
63
|
+
MarketTypeStr,
|
|
64
|
+
Map<number, PerpMarketAccount | SpotMarketAccount>
|
|
65
|
+
>();
|
|
66
|
+
|
|
67
|
+
silent = false;
|
|
68
|
+
initialized = false;
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
*
|
|
72
|
+
* @param perpMarkets The perp markets to maintain a DLOB for
|
|
73
|
+
* @param spotMarkets The spot markets to maintain a DLOB for
|
|
74
|
+
* @param silent set to true to prevent logging on inserts and removals
|
|
75
|
+
*/
|
|
76
|
+
public constructor(
|
|
77
|
+
perpMarkets: PerpMarketAccount[],
|
|
78
|
+
spotMarkets: SpotMarketAccount[],
|
|
79
|
+
silent?: boolean
|
|
80
|
+
) {
|
|
81
|
+
this.silent = silent;
|
|
82
|
+
|
|
83
|
+
this.openOrders.set('perp', new Set<string>());
|
|
84
|
+
this.openOrders.set('spot', new Set<string>());
|
|
85
|
+
this.orderLists.set('perp', new Map<number, MarketNodeLists>());
|
|
86
|
+
this.orderLists.set('spot', new Map<number, MarketNodeLists>());
|
|
87
|
+
this.marketIndexToAccount.set('perp', new Map<number, PerpMarketAccount>());
|
|
88
|
+
this.marketIndexToAccount.set('spot', new Map<number, SpotMarketAccount>());
|
|
89
|
+
|
|
90
|
+
for (const market of perpMarkets) {
|
|
91
|
+
const marketIndex = market.marketIndex;
|
|
92
|
+
this.marketIndexToAccount.get('perp').set(marketIndex.toNumber(), market);
|
|
93
|
+
|
|
94
|
+
this.orderLists.get('perp').set(marketIndex.toNumber(), {
|
|
95
|
+
limit: {
|
|
96
|
+
ask: new NodeList('limit', 'asc'),
|
|
97
|
+
bid: new NodeList('limit', 'desc'),
|
|
98
|
+
},
|
|
99
|
+
floatingLimit: {
|
|
100
|
+
ask: new NodeList('floatingLimit', 'asc'),
|
|
101
|
+
bid: new NodeList('floatingLimit', 'desc'),
|
|
102
|
+
},
|
|
103
|
+
market: {
|
|
104
|
+
ask: new NodeList('market', 'asc'),
|
|
105
|
+
bid: new NodeList('market', 'asc'), // always sort ascending for market orders
|
|
106
|
+
},
|
|
107
|
+
trigger: {
|
|
108
|
+
above: new NodeList('trigger', 'asc'),
|
|
109
|
+
below: new NodeList('trigger', 'desc'),
|
|
110
|
+
},
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
for (const market of spotMarkets) {
|
|
114
|
+
const marketIndex = market.marketIndex;
|
|
115
|
+
this.marketIndexToAccount.get('spot').set(marketIndex.toNumber(), market);
|
|
116
|
+
|
|
117
|
+
this.orderLists.get('spot').set(marketIndex.toNumber(), {
|
|
118
|
+
limit: {
|
|
119
|
+
ask: new NodeList('limit', 'asc'),
|
|
120
|
+
bid: new NodeList('limit', 'desc'),
|
|
121
|
+
},
|
|
122
|
+
floatingLimit: {
|
|
123
|
+
ask: new NodeList('floatingLimit', 'asc'),
|
|
124
|
+
bid: new NodeList('floatingLimit', 'desc'),
|
|
125
|
+
},
|
|
126
|
+
market: {
|
|
127
|
+
ask: new NodeList('market', 'asc'),
|
|
128
|
+
bid: new NodeList('market', 'asc'), // always sort ascending for market orders
|
|
129
|
+
},
|
|
130
|
+
trigger: {
|
|
131
|
+
above: new NodeList('trigger', 'asc'),
|
|
132
|
+
below: new NodeList('trigger', 'desc'),
|
|
133
|
+
},
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* initializes a new DLOB instance
|
|
140
|
+
*
|
|
141
|
+
* @param clearingHouse The ClearingHouse instance to use for price data
|
|
142
|
+
* @returns a promise that resolves when the DLOB is initialized
|
|
143
|
+
*/
|
|
144
|
+
public async init(
|
|
145
|
+
clearingHouse: ClearingHouse,
|
|
146
|
+
userMap?: UserMap
|
|
147
|
+
): Promise<boolean> {
|
|
148
|
+
if (this.initialized) {
|
|
149
|
+
return false;
|
|
150
|
+
}
|
|
151
|
+
if (userMap) {
|
|
152
|
+
// initialize the dlob with the user map (prevents hitting getProgramAccounts)
|
|
153
|
+
for (const user of userMap.values()) {
|
|
154
|
+
const userAccount = user.getUserAccount();
|
|
155
|
+
const userAccountPubkey = user.getUserAccountPublicKey();
|
|
156
|
+
|
|
157
|
+
for (const order of userAccount.orders) {
|
|
158
|
+
this.insertOrder(order, userAccountPubkey);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
} else {
|
|
162
|
+
const programAccounts = await clearingHouse.program.account.user.all();
|
|
163
|
+
for (const programAccount of programAccounts) {
|
|
164
|
+
// @ts-ignore
|
|
165
|
+
const userAccount: UserAccount = programAccount.account;
|
|
166
|
+
const userAccountPublicKey = programAccount.publicKey;
|
|
167
|
+
|
|
168
|
+
for (const order of userAccount.orders) {
|
|
169
|
+
this.insertOrder(order, userAccountPublicKey);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
this.initialized = true;
|
|
175
|
+
return true;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
public insertOrder(
|
|
179
|
+
order: Order,
|
|
180
|
+
userAccount: PublicKey,
|
|
181
|
+
onInsert?: OrderBookCallback
|
|
182
|
+
): void {
|
|
183
|
+
if (isVariant(order.status, 'init')) {
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const marketType = getVariant(order.marketType) as MarketTypeStr;
|
|
188
|
+
|
|
189
|
+
if (isVariant(order.status, 'open')) {
|
|
190
|
+
this.openOrders
|
|
191
|
+
.get(marketType)
|
|
192
|
+
.add(getOrderSignature(order.orderId, userAccount));
|
|
193
|
+
}
|
|
194
|
+
this.getListForOrder(order).insert(
|
|
195
|
+
order,
|
|
196
|
+
marketType,
|
|
197
|
+
this.marketIndexToAccount
|
|
198
|
+
.get(marketType)
|
|
199
|
+
.get(order.marketIndex.toNumber()),
|
|
200
|
+
userAccount
|
|
201
|
+
);
|
|
202
|
+
|
|
203
|
+
if (onInsert) {
|
|
204
|
+
onInsert();
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
public trigger(
|
|
209
|
+
order: Order,
|
|
210
|
+
userAccount: PublicKey,
|
|
211
|
+
onTrigger?: OrderBookCallback
|
|
212
|
+
): void {
|
|
213
|
+
if (isVariant(order.status, 'init')) {
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
const marketType = getVariant(order.marketType) as MarketTypeStr;
|
|
218
|
+
|
|
219
|
+
const triggerList = this.orderLists
|
|
220
|
+
.get(marketType)
|
|
221
|
+
.get(order.marketIndex.toNumber()).trigger[
|
|
222
|
+
isVariant(order.triggerCondition, 'above') ? 'above' : 'below'
|
|
223
|
+
];
|
|
224
|
+
triggerList.remove(order, userAccount);
|
|
225
|
+
|
|
226
|
+
this.getListForOrder(order).insert(
|
|
227
|
+
order,
|
|
228
|
+
marketType,
|
|
229
|
+
this.marketIndexToAccount
|
|
230
|
+
.get(marketType)
|
|
231
|
+
.get(order.marketIndex.toNumber()),
|
|
232
|
+
userAccount
|
|
233
|
+
);
|
|
234
|
+
if (onTrigger) {
|
|
235
|
+
onTrigger();
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
public getListForOrder(order: Order): NodeList<any> {
|
|
240
|
+
const isInactiveTriggerOrder =
|
|
241
|
+
isOneOfVariant(order.orderType, ['triggerMarket', 'triggerLimit']) &&
|
|
242
|
+
!order.triggered;
|
|
243
|
+
|
|
244
|
+
let type: DLOBNodeType;
|
|
245
|
+
if (isInactiveTriggerOrder) {
|
|
246
|
+
type = 'trigger';
|
|
247
|
+
} else if (isOneOfVariant(order.orderType, ['market', 'triggerMarket'])) {
|
|
248
|
+
type = 'market';
|
|
249
|
+
} else if (order.oraclePriceOffset.gt(ZERO)) {
|
|
250
|
+
type = 'floatingLimit';
|
|
251
|
+
} else {
|
|
252
|
+
type = 'limit';
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
let subType: string;
|
|
256
|
+
if (isInactiveTriggerOrder) {
|
|
257
|
+
subType = isVariant(order.triggerCondition, 'above') ? 'above' : 'below';
|
|
258
|
+
} else {
|
|
259
|
+
subType = isVariant(order.direction, 'long') ? 'bid' : 'ask';
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
const marketType = getVariant(order.marketType) as MarketTypeStr;
|
|
263
|
+
return this.orderLists.get(marketType).get(order.marketIndex.toNumber())[
|
|
264
|
+
type
|
|
265
|
+
][subType];
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
public findNodesToFill(
|
|
269
|
+
marketIndex: BN,
|
|
270
|
+
vBid: BN | undefined,
|
|
271
|
+
vAsk: BN | undefined,
|
|
272
|
+
slot: number,
|
|
273
|
+
marketType: MarketType,
|
|
274
|
+
oraclePriceData: OraclePriceData
|
|
275
|
+
): NodeToFill[] {
|
|
276
|
+
// Find all the crossing nodes
|
|
277
|
+
const crossingNodesToFill: Array<NodeToFill> = this.findCrossingNodesToFill(
|
|
278
|
+
marketIndex,
|
|
279
|
+
vBid,
|
|
280
|
+
vAsk,
|
|
281
|
+
slot,
|
|
282
|
+
marketType,
|
|
283
|
+
oraclePriceData
|
|
284
|
+
);
|
|
285
|
+
|
|
286
|
+
// get expired market nodes
|
|
287
|
+
const marketNodesToFill = this.findMarketNodesToFill(
|
|
288
|
+
marketIndex,
|
|
289
|
+
slot,
|
|
290
|
+
marketType
|
|
291
|
+
);
|
|
292
|
+
return crossingNodesToFill.concat(marketNodesToFill);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
public findCrossingNodesToFill(
|
|
296
|
+
marketIndex: BN,
|
|
297
|
+
vBid: BN | undefined,
|
|
298
|
+
vAsk: BN | undefined,
|
|
299
|
+
slot: number,
|
|
300
|
+
marketType: MarketType,
|
|
301
|
+
oraclePriceData: OraclePriceData
|
|
302
|
+
): NodeToFill[] {
|
|
303
|
+
const nodesToFill = new Array<NodeToFill>();
|
|
304
|
+
|
|
305
|
+
const askGenerator = this.getAsks(
|
|
306
|
+
marketIndex,
|
|
307
|
+
vAsk,
|
|
308
|
+
slot,
|
|
309
|
+
marketType,
|
|
310
|
+
oraclePriceData
|
|
311
|
+
);
|
|
312
|
+
const bidGenerator = this.getBids(
|
|
313
|
+
marketIndex,
|
|
314
|
+
vBid,
|
|
315
|
+
slot,
|
|
316
|
+
marketType,
|
|
317
|
+
oraclePriceData
|
|
318
|
+
);
|
|
319
|
+
|
|
320
|
+
let nextAsk = askGenerator.next();
|
|
321
|
+
let nextBid = bidGenerator.next();
|
|
322
|
+
|
|
323
|
+
// First try to find orders that cross
|
|
324
|
+
while (!nextAsk.done && !nextBid.done) {
|
|
325
|
+
const { crossingNodes, exhaustedSide } = this.findCrossingOrders(
|
|
326
|
+
nextAsk.value,
|
|
327
|
+
nextBid.value,
|
|
328
|
+
oraclePriceData,
|
|
329
|
+
slot
|
|
330
|
+
);
|
|
331
|
+
|
|
332
|
+
if (exhaustedSide === 'bid') {
|
|
333
|
+
nextBid = bidGenerator.next();
|
|
334
|
+
} else if (exhaustedSide === 'ask') {
|
|
335
|
+
nextAsk = askGenerator.next();
|
|
336
|
+
} else if (exhaustedSide === 'both') {
|
|
337
|
+
nextBid = bidGenerator.next();
|
|
338
|
+
nextAsk = askGenerator.next();
|
|
339
|
+
} else {
|
|
340
|
+
console.error(`invalid exhaustedSide: ${exhaustedSide}`);
|
|
341
|
+
break;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
const takerIsMaker =
|
|
345
|
+
crossingNodes?.makerNode !== undefined &&
|
|
346
|
+
crossingNodes.node.userAccount.equals(
|
|
347
|
+
crossingNodes.makerNode.userAccount
|
|
348
|
+
);
|
|
349
|
+
|
|
350
|
+
// Verify that each side is different user
|
|
351
|
+
if (crossingNodes && !takerIsMaker) {
|
|
352
|
+
nodesToFill.push(crossingNodes);
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
return nodesToFill;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
public findMarketNodesToFill(
|
|
359
|
+
marketIndex: BN,
|
|
360
|
+
slot: number,
|
|
361
|
+
marketType: MarketType
|
|
362
|
+
): NodeToFill[] {
|
|
363
|
+
const nodesToFill = new Array<NodeToFill>();
|
|
364
|
+
// Then see if there are orders to fill against vamm
|
|
365
|
+
for (const marketBid of this.getMarketBids(marketIndex, marketType)) {
|
|
366
|
+
if (
|
|
367
|
+
isAuctionComplete(marketBid.order, slot) ||
|
|
368
|
+
isOrderExpired(marketBid.order, slot)
|
|
369
|
+
) {
|
|
370
|
+
nodesToFill.push({
|
|
371
|
+
node: marketBid,
|
|
372
|
+
});
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
for (const marketAsk of this.getMarketAsks(marketIndex, marketType)) {
|
|
377
|
+
if (
|
|
378
|
+
isAuctionComplete(marketAsk.order, slot) ||
|
|
379
|
+
isOrderExpired(marketAsk.order, slot)
|
|
380
|
+
) {
|
|
381
|
+
nodesToFill.push({
|
|
382
|
+
node: marketAsk,
|
|
383
|
+
});
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
return nodesToFill;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
public findJitAuctionNodesToFill(
|
|
390
|
+
marketIndex: BN,
|
|
391
|
+
slot: number,
|
|
392
|
+
marketType: MarketType
|
|
393
|
+
): NodeToFill[] {
|
|
394
|
+
const nodesToFill = new Array<NodeToFill>();
|
|
395
|
+
// Then see if there are orders still in JIT auction
|
|
396
|
+
for (const marketBid of this.getMarketBids(marketIndex, marketType)) {
|
|
397
|
+
if (!isAuctionComplete(marketBid.order, slot)) {
|
|
398
|
+
nodesToFill.push({
|
|
399
|
+
node: marketBid,
|
|
400
|
+
});
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
for (const marketAsk of this.getMarketAsks(marketIndex, marketType)) {
|
|
405
|
+
if (!isAuctionComplete(marketAsk.order, slot)) {
|
|
406
|
+
nodesToFill.push({
|
|
407
|
+
node: marketAsk,
|
|
408
|
+
});
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
return nodesToFill;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
public getMarketBids(
|
|
415
|
+
marketIndex: BN,
|
|
416
|
+
marketType: MarketType
|
|
417
|
+
): Generator<DLOBNode> {
|
|
418
|
+
const marketTypeStr = getVariant(marketType) as MarketTypeStr;
|
|
419
|
+
return this.orderLists
|
|
420
|
+
.get(marketTypeStr)
|
|
421
|
+
.get(marketIndex.toNumber())
|
|
422
|
+
.market.bid.getGenerator();
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
public getMarketAsks(
|
|
426
|
+
marketIndex: BN,
|
|
427
|
+
marketType: MarketType
|
|
428
|
+
): Generator<DLOBNode> {
|
|
429
|
+
const marketTypeStr = getVariant(marketType) as MarketTypeStr;
|
|
430
|
+
return this.orderLists
|
|
431
|
+
.get(marketTypeStr)
|
|
432
|
+
.get(marketIndex.toNumber())
|
|
433
|
+
.market.ask.getGenerator();
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
*getAsks(
|
|
437
|
+
marketIndex: BN,
|
|
438
|
+
vAsk: BN | undefined,
|
|
439
|
+
slot: number,
|
|
440
|
+
marketType: MarketType,
|
|
441
|
+
oraclePriceData: OraclePriceData
|
|
442
|
+
): Generator<DLOBNode> {
|
|
443
|
+
if (isVariant(marketType, 'spot') && !oraclePriceData) {
|
|
444
|
+
throw new Error('Must provide OraclePriceData to get spot asks');
|
|
445
|
+
}
|
|
446
|
+
const marketTypeStr = getVariant(marketType) as MarketTypeStr;
|
|
447
|
+
const nodeLists = this.orderLists
|
|
448
|
+
.get(marketTypeStr)
|
|
449
|
+
.get(marketIndex.toNumber());
|
|
450
|
+
|
|
451
|
+
const generatorList = [
|
|
452
|
+
nodeLists.limit.ask.getGenerator(),
|
|
453
|
+
nodeLists.floatingLimit.ask.getGenerator(),
|
|
454
|
+
nodeLists.market.ask.getGenerator(),
|
|
455
|
+
];
|
|
456
|
+
|
|
457
|
+
if (marketTypeStr === 'perp') {
|
|
458
|
+
generatorList.push(getVammNodeGenerator(vAsk));
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
const askGenerators = generatorList.map((generator) => {
|
|
462
|
+
return {
|
|
463
|
+
next: generator.next(),
|
|
464
|
+
generator,
|
|
465
|
+
};
|
|
466
|
+
});
|
|
467
|
+
|
|
468
|
+
let asksExhausted = false;
|
|
469
|
+
while (!asksExhausted) {
|
|
470
|
+
const bestGenerator = askGenerators.reduce(
|
|
471
|
+
(bestGenerator, currentGenerator) => {
|
|
472
|
+
if (currentGenerator.next.done) {
|
|
473
|
+
return bestGenerator;
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
if (bestGenerator.next.done) {
|
|
477
|
+
return currentGenerator;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
const bestAskPrice = bestGenerator.next.value.getPrice(
|
|
481
|
+
oraclePriceData,
|
|
482
|
+
slot
|
|
483
|
+
);
|
|
484
|
+
const currentAskPrice = currentGenerator.next.value.getPrice(
|
|
485
|
+
oraclePriceData,
|
|
486
|
+
slot
|
|
487
|
+
);
|
|
488
|
+
|
|
489
|
+
return bestAskPrice.lt(currentAskPrice)
|
|
490
|
+
? bestGenerator
|
|
491
|
+
: currentGenerator;
|
|
492
|
+
}
|
|
493
|
+
);
|
|
494
|
+
|
|
495
|
+
if (!bestGenerator.next.done) {
|
|
496
|
+
yield bestGenerator.next.value;
|
|
497
|
+
bestGenerator.next = bestGenerator.generator.next();
|
|
498
|
+
} else {
|
|
499
|
+
asksExhausted = true;
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
*getBids(
|
|
505
|
+
marketIndex: BN,
|
|
506
|
+
vBid: BN | undefined,
|
|
507
|
+
slot: number,
|
|
508
|
+
marketType: MarketType,
|
|
509
|
+
oraclePriceData: OraclePriceData
|
|
510
|
+
): Generator<DLOBNode> {
|
|
511
|
+
if (isVariant(marketType, 'spot') && !oraclePriceData) {
|
|
512
|
+
throw new Error('Must provide OraclePriceData to get spot bids');
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
const marketTypeStr = getVariant(marketType) as MarketTypeStr;
|
|
516
|
+
const nodeLists = this.orderLists
|
|
517
|
+
.get(marketTypeStr)
|
|
518
|
+
.get(marketIndex.toNumber());
|
|
519
|
+
|
|
520
|
+
const generatorList = [
|
|
521
|
+
nodeLists.limit.bid.getGenerator(),
|
|
522
|
+
nodeLists.floatingLimit.bid.getGenerator(),
|
|
523
|
+
nodeLists.market.bid.getGenerator(),
|
|
524
|
+
];
|
|
525
|
+
if (marketTypeStr === 'perp') {
|
|
526
|
+
generatorList.push(getVammNodeGenerator(vBid));
|
|
527
|
+
}
|
|
528
|
+
const bidGenerators = generatorList.map((generator) => {
|
|
529
|
+
return {
|
|
530
|
+
next: generator.next(),
|
|
531
|
+
generator,
|
|
532
|
+
};
|
|
533
|
+
});
|
|
534
|
+
|
|
535
|
+
let bidsExhausted = false; // there will always be the vBid
|
|
536
|
+
while (!bidsExhausted) {
|
|
537
|
+
const bestGenerator = bidGenerators.reduce(
|
|
538
|
+
(bestGenerator, currentGenerator) => {
|
|
539
|
+
if (currentGenerator.next.done) {
|
|
540
|
+
return bestGenerator;
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
if (bestGenerator.next.done) {
|
|
544
|
+
return currentGenerator;
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
const bestBidPrice = bestGenerator.next.value.getPrice(
|
|
548
|
+
oraclePriceData,
|
|
549
|
+
slot
|
|
550
|
+
);
|
|
551
|
+
const currentBidPrice = currentGenerator.next.value.getPrice(
|
|
552
|
+
oraclePriceData,
|
|
553
|
+
slot
|
|
554
|
+
);
|
|
555
|
+
|
|
556
|
+
return bestBidPrice.gt(currentBidPrice)
|
|
557
|
+
? bestGenerator
|
|
558
|
+
: currentGenerator;
|
|
559
|
+
}
|
|
560
|
+
);
|
|
561
|
+
|
|
562
|
+
if (!bestGenerator.next.done) {
|
|
563
|
+
yield bestGenerator.next.value;
|
|
564
|
+
bestGenerator.next = bestGenerator.generator.next();
|
|
565
|
+
} else {
|
|
566
|
+
bidsExhausted = true;
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
findCrossingOrders(
|
|
572
|
+
askNode: DLOBNode,
|
|
573
|
+
bidNode: DLOBNode,
|
|
574
|
+
oraclePriceData: OraclePriceData,
|
|
575
|
+
slot: number
|
|
576
|
+
): {
|
|
577
|
+
crossingNodes?: NodeToFill;
|
|
578
|
+
exhaustedSide?: Side;
|
|
579
|
+
} {
|
|
580
|
+
const bidPrice = bidNode.getPrice(oraclePriceData, slot);
|
|
581
|
+
const askPrice = askNode.getPrice(oraclePriceData, slot);
|
|
582
|
+
|
|
583
|
+
const bidOrder = bidNode.order;
|
|
584
|
+
const askOrder = askNode.order;
|
|
585
|
+
|
|
586
|
+
const containsMarketOrder =
|
|
587
|
+
(askOrder && isVariant(askOrder.orderType, 'market')) ||
|
|
588
|
+
(bidOrder && isVariant(bidOrder.orderType, 'market'));
|
|
589
|
+
|
|
590
|
+
if (!containsMarketOrder || bidPrice.lt(askPrice)) {
|
|
591
|
+
// no market orders, and no crossing orders - return
|
|
592
|
+
if (askNode.isVammNode() && !bidNode.isVammNode()) {
|
|
593
|
+
return {
|
|
594
|
+
exhaustedSide: 'bid',
|
|
595
|
+
};
|
|
596
|
+
} else if (!askNode.isVammNode() && bidNode.isVammNode()) {
|
|
597
|
+
return {
|
|
598
|
+
exhaustedSide: 'ask',
|
|
599
|
+
};
|
|
600
|
+
} else {
|
|
601
|
+
return {
|
|
602
|
+
exhaustedSide: 'both',
|
|
603
|
+
};
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
// User bid crosses the vamm ask
|
|
608
|
+
if (askNode.isVammNode()) {
|
|
609
|
+
if (!isAuctionComplete(bidOrder, slot)) {
|
|
610
|
+
return {
|
|
611
|
+
exhaustedSide: 'bid',
|
|
612
|
+
};
|
|
613
|
+
}
|
|
614
|
+
return {
|
|
615
|
+
crossingNodes: {
|
|
616
|
+
node: bidNode,
|
|
617
|
+
},
|
|
618
|
+
exhaustedSide: 'bid',
|
|
619
|
+
};
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
// User ask crosses the vamm bid
|
|
623
|
+
if (bidNode.isVammNode()) {
|
|
624
|
+
if (!isAuctionComplete(askOrder, slot)) {
|
|
625
|
+
return {
|
|
626
|
+
exhaustedSide: 'ask',
|
|
627
|
+
};
|
|
628
|
+
}
|
|
629
|
+
return {
|
|
630
|
+
crossingNodes: {
|
|
631
|
+
node: askNode,
|
|
632
|
+
},
|
|
633
|
+
exhaustedSide: 'ask',
|
|
634
|
+
};
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
const bidBaseRemaining = bidOrder.baseAssetAmount.sub(
|
|
638
|
+
bidOrder.baseAssetAmountFilled
|
|
639
|
+
);
|
|
640
|
+
const askBaseRemaining = askOrder.baseAssetAmount.sub(
|
|
641
|
+
askOrder.baseAssetAmountFilled
|
|
642
|
+
);
|
|
643
|
+
|
|
644
|
+
let exhaustedSide: Side;
|
|
645
|
+
if (bidBaseRemaining.eq(askBaseRemaining)) {
|
|
646
|
+
exhaustedSide = 'both';
|
|
647
|
+
} else if (bidBaseRemaining.gt(askBaseRemaining)) {
|
|
648
|
+
exhaustedSide = 'ask';
|
|
649
|
+
} else {
|
|
650
|
+
exhaustedSide = 'bid';
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
// Can't match two maker orders
|
|
654
|
+
if (bidOrder.postOnly && askOrder.postOnly) {
|
|
655
|
+
return {
|
|
656
|
+
exhaustedSide,
|
|
657
|
+
};
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
// update the orders as if they fill - so we don't try to match them on the next iteration
|
|
661
|
+
if (exhaustedSide === 'ask') {
|
|
662
|
+
bidNode.order.baseAssetAmountFilled =
|
|
663
|
+
bidOrder.baseAssetAmountFilled.add(askBaseRemaining);
|
|
664
|
+
askNode.order.baseAssetAmountFilled = askOrder.baseAssetAmount;
|
|
665
|
+
} else if (exhaustedSide === 'bid') {
|
|
666
|
+
askNode.order.baseAssetAmountFilled =
|
|
667
|
+
askOrder.baseAssetAmountFilled.add(bidBaseRemaining);
|
|
668
|
+
bidNode.order.baseAssetAmountFilled = bidOrder.baseAssetAmount;
|
|
669
|
+
} else {
|
|
670
|
+
askNode.order.baseAssetAmountFilled = askOrder.baseAssetAmount;
|
|
671
|
+
bidNode.order.baseAssetAmountFilled = bidOrder.baseAssetAmount;
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
// Bid is maker
|
|
675
|
+
if (bidOrder.postOnly) {
|
|
676
|
+
return {
|
|
677
|
+
crossingNodes: {
|
|
678
|
+
node: askNode,
|
|
679
|
+
makerNode: bidNode,
|
|
680
|
+
},
|
|
681
|
+
exhaustedSide,
|
|
682
|
+
};
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
// Ask is maker
|
|
686
|
+
if (askOrder.postOnly) {
|
|
687
|
+
return {
|
|
688
|
+
crossingNodes: {
|
|
689
|
+
node: bidNode,
|
|
690
|
+
makerNode: askNode,
|
|
691
|
+
},
|
|
692
|
+
exhaustedSide,
|
|
693
|
+
};
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
// Both are takers
|
|
697
|
+
// older order is maker
|
|
698
|
+
const [olderNode, newerNode] = askOrder.ts.lt(bidOrder.ts)
|
|
699
|
+
? [askNode, bidNode]
|
|
700
|
+
: [bidNode, askNode];
|
|
701
|
+
return {
|
|
702
|
+
crossingNodes: {
|
|
703
|
+
node: newerNode,
|
|
704
|
+
makerNode: olderNode,
|
|
705
|
+
},
|
|
706
|
+
exhaustedSide,
|
|
707
|
+
};
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
public getBestAsk(
|
|
711
|
+
marketIndex: BN,
|
|
712
|
+
vAsk: BN | undefined,
|
|
713
|
+
slot: number,
|
|
714
|
+
marketType: MarketType,
|
|
715
|
+
oraclePriceData: OraclePriceData
|
|
716
|
+
): BN {
|
|
717
|
+
return this.getAsks(marketIndex, vAsk, slot, marketType, oraclePriceData)
|
|
718
|
+
.next()
|
|
719
|
+
.value.getPrice(oraclePriceData, slot);
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
public getBestBid(
|
|
723
|
+
marketIndex: BN,
|
|
724
|
+
vBid: BN | undefined,
|
|
725
|
+
slot: number,
|
|
726
|
+
marketType: MarketType,
|
|
727
|
+
oraclePriceData: OraclePriceData
|
|
728
|
+
): BN {
|
|
729
|
+
return this.getBids(marketIndex, vBid, slot, marketType, oraclePriceData)
|
|
730
|
+
.next()
|
|
731
|
+
.value.getPrice(oraclePriceData, slot);
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
public findNodesToTrigger(
|
|
735
|
+
marketIndex: BN,
|
|
736
|
+
slot: number,
|
|
737
|
+
oraclePrice: BN,
|
|
738
|
+
marketType: MarketType
|
|
739
|
+
): NodeToTrigger[] {
|
|
740
|
+
const nodesToTrigger = [];
|
|
741
|
+
const marketTypeStr = getVariant(marketType) as MarketTypeStr;
|
|
742
|
+
for (const node of this.orderLists
|
|
743
|
+
.get(marketTypeStr)
|
|
744
|
+
.get(marketIndex.toNumber())
|
|
745
|
+
.trigger.above.getGenerator()) {
|
|
746
|
+
if (oraclePrice.gt(node.order.triggerPrice)) {
|
|
747
|
+
if (isAuctionComplete(node.order, slot)) {
|
|
748
|
+
nodesToTrigger.push({
|
|
749
|
+
node: node,
|
|
750
|
+
});
|
|
751
|
+
}
|
|
752
|
+
} else {
|
|
753
|
+
break;
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
for (const node of this.orderLists
|
|
758
|
+
.get(marketTypeStr)
|
|
759
|
+
.get(marketIndex.toNumber())
|
|
760
|
+
.trigger.below.getGenerator()) {
|
|
761
|
+
if (oraclePrice.lt(node.order.triggerPrice)) {
|
|
762
|
+
if (isAuctionComplete(node.order, slot)) {
|
|
763
|
+
nodesToTrigger.push({
|
|
764
|
+
node: node,
|
|
765
|
+
});
|
|
766
|
+
}
|
|
767
|
+
} else {
|
|
768
|
+
break;
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
return nodesToTrigger;
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
public printTopOfOrderLists(
|
|
776
|
+
sdkConfig: any,
|
|
777
|
+
clearingHouse: ClearingHouse,
|
|
778
|
+
slotSubscriber: SlotSubscriber,
|
|
779
|
+
marketIndex: BN,
|
|
780
|
+
marketType: MarketType
|
|
781
|
+
) {
|
|
782
|
+
if (isVariant(marketType, 'perp')) {
|
|
783
|
+
const market = clearingHouse.getPerpMarketAccount(marketIndex);
|
|
784
|
+
|
|
785
|
+
const slot = slotSubscriber.getSlot();
|
|
786
|
+
const oraclePriceData = clearingHouse.getOracleDataForMarket(marketIndex);
|
|
787
|
+
const vAsk = calculateAskPrice(market, oraclePriceData);
|
|
788
|
+
const vBid = calculateBidPrice(market, oraclePriceData);
|
|
789
|
+
|
|
790
|
+
const bestAsk = this.getBestAsk(
|
|
791
|
+
marketIndex,
|
|
792
|
+
vAsk,
|
|
793
|
+
slot,
|
|
794
|
+
marketType,
|
|
795
|
+
oraclePriceData
|
|
796
|
+
);
|
|
797
|
+
const bestBid = this.getBestBid(
|
|
798
|
+
marketIndex,
|
|
799
|
+
vBid,
|
|
800
|
+
slot,
|
|
801
|
+
marketType,
|
|
802
|
+
oraclePriceData
|
|
803
|
+
);
|
|
804
|
+
const mid = bestAsk.add(bestBid).div(new BN(2));
|
|
805
|
+
|
|
806
|
+
const bidSpread =
|
|
807
|
+
(convertToNumber(bestBid, MARK_PRICE_PRECISION) /
|
|
808
|
+
convertToNumber(oraclePriceData.price, MARK_PRICE_PRECISION) -
|
|
809
|
+
1) *
|
|
810
|
+
100.0;
|
|
811
|
+
const askSpread =
|
|
812
|
+
(convertToNumber(bestAsk, MARK_PRICE_PRECISION) /
|
|
813
|
+
convertToNumber(oraclePriceData.price, MARK_PRICE_PRECISION) -
|
|
814
|
+
1) *
|
|
815
|
+
100.0;
|
|
816
|
+
|
|
817
|
+
console.log(
|
|
818
|
+
`Market ${sdkConfig.MARKETS[marketIndex.toNumber()].symbol} Orders`
|
|
819
|
+
);
|
|
820
|
+
console.log(
|
|
821
|
+
` Ask`,
|
|
822
|
+
convertToNumber(bestAsk, MARK_PRICE_PRECISION).toFixed(3),
|
|
823
|
+
`(${askSpread.toFixed(4)}%)`
|
|
824
|
+
);
|
|
825
|
+
console.log(
|
|
826
|
+
` Mid`,
|
|
827
|
+
convertToNumber(mid, MARK_PRICE_PRECISION).toFixed(3)
|
|
828
|
+
);
|
|
829
|
+
console.log(
|
|
830
|
+
` Bid`,
|
|
831
|
+
convertToNumber(bestBid, MARK_PRICE_PRECISION).toFixed(3),
|
|
832
|
+
`(${bidSpread.toFixed(4)}%)`
|
|
833
|
+
);
|
|
834
|
+
} else if (isVariant(marketType, 'spot')) {
|
|
835
|
+
const slot = slotSubscriber.getSlot();
|
|
836
|
+
const oraclePriceData = clearingHouse.getOracleDataForMarket(marketIndex);
|
|
837
|
+
|
|
838
|
+
const bestAsk = this.getBestAsk(
|
|
839
|
+
marketIndex,
|
|
840
|
+
undefined,
|
|
841
|
+
slot,
|
|
842
|
+
marketType,
|
|
843
|
+
oraclePriceData
|
|
844
|
+
);
|
|
845
|
+
const bestBid = this.getBestBid(
|
|
846
|
+
marketIndex,
|
|
847
|
+
undefined,
|
|
848
|
+
slot,
|
|
849
|
+
marketType,
|
|
850
|
+
oraclePriceData
|
|
851
|
+
);
|
|
852
|
+
const mid = bestAsk.add(bestBid).div(new BN(2));
|
|
853
|
+
|
|
854
|
+
const bidSpread =
|
|
855
|
+
(convertToNumber(bestBid, MARK_PRICE_PRECISION) /
|
|
856
|
+
convertToNumber(oraclePriceData.price, MARK_PRICE_PRECISION) -
|
|
857
|
+
1) *
|
|
858
|
+
100.0;
|
|
859
|
+
const askSpread =
|
|
860
|
+
(convertToNumber(bestAsk, MARK_PRICE_PRECISION) /
|
|
861
|
+
convertToNumber(oraclePriceData.price, MARK_PRICE_PRECISION) -
|
|
862
|
+
1) *
|
|
863
|
+
100.0;
|
|
864
|
+
|
|
865
|
+
console.log(
|
|
866
|
+
`Market ${sdkConfig.MARKETS[marketIndex.toNumber()].symbol} Orders`
|
|
867
|
+
);
|
|
868
|
+
console.log(
|
|
869
|
+
` Ask`,
|
|
870
|
+
convertToNumber(bestAsk, MARK_PRICE_PRECISION).toFixed(3),
|
|
871
|
+
`(${askSpread.toFixed(4)}%)`
|
|
872
|
+
);
|
|
873
|
+
console.log(
|
|
874
|
+
` Mid`,
|
|
875
|
+
convertToNumber(mid, MARK_PRICE_PRECISION).toFixed(3)
|
|
876
|
+
);
|
|
877
|
+
console.log(
|
|
878
|
+
` Bid`,
|
|
879
|
+
convertToNumber(bestBid, MARK_PRICE_PRECISION).toFixed(3),
|
|
880
|
+
`(${bidSpread.toFixed(4)}%)`
|
|
881
|
+
);
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
}
|