nodejs-order-book 10.0.0 → 10.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +317 -40
- package/dist/cjs/errors.js +47 -45
- package/dist/cjs/index.js +4 -3
- package/dist/cjs/order.js +229 -272
- package/dist/cjs/orderbook.js +848 -782
- package/dist/cjs/orderqueue.js +68 -66
- package/dist/cjs/orderside.js +139 -147
- package/dist/cjs/stopbook.js +69 -69
- package/dist/cjs/stopqueue.js +47 -51
- package/dist/cjs/stopside.js +68 -68
- package/dist/cjs/types.js +8 -1
- package/dist/cjs/utils.js +4 -5
- package/dist/esm/errors.js +47 -46
- package/dist/esm/index.js +2 -2
- package/dist/esm/order.js +227 -273
- package/dist/esm/orderbook.js +844 -779
- package/dist/esm/orderqueue.js +67 -66
- package/dist/esm/orderside.js +134 -143
- package/dist/esm/stopbook.js +67 -68
- package/dist/esm/stopqueue.js +46 -51
- package/dist/esm/stopside.js +64 -65
- package/dist/esm/types.js +7 -0
- package/dist/esm/utils.js +4 -5
- package/dist/types/errors.d.ts +2 -1
- package/dist/types/index.d.ts +3 -3
- package/dist/types/order.d.ts +5 -1
- package/dist/types/types.d.ts +24 -0
- package/dist/types/utils.d.ts +1 -1
- package/package.json +13 -12
package/README.md
CHANGED
|
@@ -29,6 +29,7 @@ Ultra-fast Node.js Order Book written in TypeScript </br> for high-frequency tra
|
|
|
29
29
|
- [Create OCO (One-Cancels-the-Other) order `oco()`](#create-oco-one-cancels-the-other-order)
|
|
30
30
|
- [Modify an existing order `modifiy()`](#modify-an-existing-order)
|
|
31
31
|
- [Cancel order `cancel()`](#cancel-order)
|
|
32
|
+
- [Self-Trade Prevention (STP)](#self-trade-prevention-stp)
|
|
32
33
|
- [Order Book Options](#order-book-options)
|
|
33
34
|
- [Snapshot](#snapshot)
|
|
34
35
|
- [Journal Logs](#journal-logs)
|
|
@@ -50,6 +51,7 @@ Ultra-fast Node.js Order Book written in TypeScript </br> for high-frequency tra
|
|
|
50
51
|
- Supports `post-only` limit order <img src="https://img.shields.io/badge/New-green" alt="New">
|
|
51
52
|
- Supports conditional orders [**Stop Limit, Stop Market and OCO**](#conditional-orders) <img src="https://img.shields.io/badge/New-green" alt="New">
|
|
52
53
|
- Supports time in force GTC, FOK and IOC <img src="https://img.shields.io/badge/New-green" alt="New">
|
|
54
|
+
- Support [Self-Trade Prevention (STP)](#self-trade-prevention-stp)
|
|
53
55
|
- Supports order cancelling
|
|
54
56
|
- Supports order price and/or size updating <img src="https://img.shields.io/badge/New-green" alt="New">
|
|
55
57
|
- Snapshot and journaling functionalities for restoring the order book during server startup <img src="https://img.shields.io/badge/New-green" alt="New">
|
|
@@ -63,19 +65,19 @@ Ultra-fast Node.js Order Book written in TypeScript </br> for high-frequency tra
|
|
|
63
65
|
|
|
64
66
|
Install with npm:
|
|
65
67
|
|
|
66
|
-
```
|
|
68
|
+
```
|
|
67
69
|
npm install nodejs-order-book
|
|
68
70
|
```
|
|
69
71
|
|
|
70
72
|
Install with yarn:
|
|
71
73
|
|
|
72
|
-
```
|
|
74
|
+
```
|
|
73
75
|
yarn add nodejs-order-book
|
|
74
76
|
```
|
|
75
77
|
|
|
76
78
|
Install with pnpm:
|
|
77
79
|
|
|
78
|
-
```
|
|
80
|
+
```
|
|
79
81
|
pnpm add nodejs-order-book
|
|
80
82
|
```
|
|
81
83
|
|
|
@@ -83,7 +85,7 @@ pnpm add nodejs-order-book
|
|
|
83
85
|
|
|
84
86
|
To start using order book you need to import `OrderBook` and create new instance:
|
|
85
87
|
|
|
86
|
-
```
|
|
88
|
+
```ts
|
|
87
89
|
import { OrderBook } from 'nodejs-order-book'
|
|
88
90
|
|
|
89
91
|
const ob = new OrderBook()
|
|
@@ -91,20 +93,39 @@ const ob = new OrderBook()
|
|
|
91
93
|
|
|
92
94
|
Then you'll be able to use next primary functions:
|
|
93
95
|
|
|
94
|
-
```
|
|
95
|
-
ob.createOrder({
|
|
96
|
+
```ts
|
|
97
|
+
ob.createOrder({
|
|
98
|
+
type: 'limit' | 'market',
|
|
99
|
+
side: 'buy' | 'sell',
|
|
100
|
+
size: number,
|
|
101
|
+
price?: number,
|
|
102
|
+
id?: string,
|
|
103
|
+
postOnly?: boolean,
|
|
104
|
+
timeInForce?: 'GTC' | 'FOK' | 'IOC'
|
|
105
|
+
})
|
|
96
106
|
|
|
97
|
-
ob.limit({
|
|
107
|
+
ob.limit({
|
|
108
|
+
id: string,
|
|
109
|
+
side: 'buy' | 'sell',
|
|
110
|
+
size: number,
|
|
111
|
+
price: number,
|
|
112
|
+
postOnly?: boolean,
|
|
113
|
+
timeInForce?: 'GTC' | 'FOK' | 'IOC'
|
|
114
|
+
})
|
|
98
115
|
|
|
99
116
|
ob.market({ side: 'buy' | 'sell', size: number })
|
|
100
117
|
|
|
101
|
-
ob.modify(orderID: string, {
|
|
118
|
+
ob.modify(orderID: string, {
|
|
119
|
+
side: 'buy' | 'sell',
|
|
120
|
+
size: number,
|
|
121
|
+
price: number
|
|
122
|
+
})
|
|
102
123
|
|
|
103
124
|
ob.cancel(orderID: string)
|
|
104
125
|
```
|
|
105
126
|
### Conditional Orders
|
|
106
127
|
Currently `Stop Market`, `Stop Limit` and `OCO` orders are supported.
|
|
107
|
-
```
|
|
128
|
+
```ts
|
|
108
129
|
import { OrderBook } from 'nodejs-order-book'
|
|
109
130
|
|
|
110
131
|
const ob = new OrderBook()
|
|
@@ -153,26 +174,59 @@ To add an order to the order book you can call the general `createOrder()` funct
|
|
|
153
174
|
|
|
154
175
|
### Create Order
|
|
155
176
|
|
|
156
|
-
```
|
|
177
|
+
```ts
|
|
157
178
|
// Create limit order
|
|
158
|
-
ob.createOrder({
|
|
179
|
+
ob.createOrder({
|
|
180
|
+
type: 'limit',
|
|
181
|
+
side: 'buy' | 'sell',
|
|
182
|
+
size: number,
|
|
183
|
+
price: number,
|
|
184
|
+
id: string,
|
|
185
|
+
postOnly?: boolean,
|
|
186
|
+
timeInForce?: 'GTC' | 'FOK' | 'IOC'
|
|
187
|
+
})
|
|
159
188
|
|
|
160
189
|
// Create market order
|
|
161
|
-
ob.createOrder({
|
|
190
|
+
ob.createOrder({
|
|
191
|
+
type: 'market',
|
|
192
|
+
side: 'buy' | 'sell',
|
|
193
|
+
size: number
|
|
194
|
+
})
|
|
162
195
|
|
|
163
196
|
// Create stop limit order
|
|
164
|
-
ob.createOrder({
|
|
197
|
+
ob.createOrder({
|
|
198
|
+
type: 'stop_limit',
|
|
199
|
+
side: 'buy' | 'sell',
|
|
200
|
+
size: number,
|
|
201
|
+
price: number,
|
|
202
|
+
id: string,
|
|
203
|
+
stopPrice: number,
|
|
204
|
+
timeInForce?: 'GTC' | 'FOK' | 'IOC'
|
|
205
|
+
})
|
|
165
206
|
|
|
166
207
|
// Create stop market order
|
|
167
|
-
ob.createOrder({
|
|
208
|
+
ob.createOrder({
|
|
209
|
+
type: 'stop_market',
|
|
210
|
+
side: 'buy' | 'sell',
|
|
211
|
+
size: number,
|
|
212
|
+
stopPrice: number
|
|
213
|
+
})
|
|
168
214
|
|
|
169
215
|
// Create OCO order
|
|
170
|
-
ob.createOrder({
|
|
216
|
+
ob.createOrder({
|
|
217
|
+
type: 'oco',
|
|
218
|
+
side: 'buy' | 'sell',
|
|
219
|
+
size: number,
|
|
220
|
+
stopPrice: number,
|
|
221
|
+
stopLimitPrice: number,
|
|
222
|
+
timeInForce?: 'GTC' | 'FOK' | 'IOC',
|
|
223
|
+
stopLimitTimeInForce?: 'GTC' | 'FOK' | 'IOC'
|
|
224
|
+
})
|
|
171
225
|
```
|
|
172
226
|
|
|
173
227
|
### Create Limit Order
|
|
174
228
|
|
|
175
|
-
```
|
|
229
|
+
```ts
|
|
176
230
|
/**
|
|
177
231
|
* Create a limit order. See {@link LimitOrderOptions} for details.
|
|
178
232
|
*
|
|
@@ -185,12 +239,19 @@ ob.createOrder({ type: 'oco', side: 'buy' | 'sell', size: number, stopPrice: num
|
|
|
185
239
|
* @param options.timeInForce - Time-in-force type supported are: GTC, FOK, IOC. Default is GTC
|
|
186
240
|
* @returns An object with the result of the processed order or an error. See {@link IProcessOrder} for the returned data structure
|
|
187
241
|
*/
|
|
188
|
-
ob.limit({
|
|
242
|
+
ob.limit({
|
|
243
|
+
side: 'buy' | 'sell',
|
|
244
|
+
id: string,
|
|
245
|
+
size: number,
|
|
246
|
+
price: number,
|
|
247
|
+
postOnly?: boolean,
|
|
248
|
+
timeInForce?: 'GTC' | 'FOK' | 'IOC'
|
|
249
|
+
})
|
|
189
250
|
```
|
|
190
251
|
|
|
191
252
|
For example:
|
|
192
253
|
|
|
193
|
-
```
|
|
254
|
+
```ts
|
|
194
255
|
ob.limit({ side: "sell", id: "uniqueID", size: 55, price: 100 })
|
|
195
256
|
|
|
196
257
|
asks: 110 -> 5 110 -> 5
|
|
@@ -203,7 +264,7 @@ done - null
|
|
|
203
264
|
partial - null
|
|
204
265
|
```
|
|
205
266
|
|
|
206
|
-
```
|
|
267
|
+
```ts
|
|
207
268
|
ob.limit({ side: "buy", id: "uniqueID", size: 7, price: 120 })
|
|
208
269
|
|
|
209
270
|
asks: 110 -> 5
|
|
@@ -217,7 +278,7 @@ done - 2 (or more orders)
|
|
|
217
278
|
partial - uniqueID order
|
|
218
279
|
```
|
|
219
280
|
|
|
220
|
-
```
|
|
281
|
+
```ts
|
|
221
282
|
ob.limit({ side: "buy", id: "uniqueID", size: 3, price: 120 })
|
|
222
283
|
|
|
223
284
|
asks: 110 -> 5
|
|
@@ -232,7 +293,7 @@ partial - 1 order with price 110
|
|
|
232
293
|
|
|
233
294
|
### Create Market Order
|
|
234
295
|
|
|
235
|
-
```
|
|
296
|
+
```ts
|
|
236
297
|
/**
|
|
237
298
|
* Create a market order. See {@link MarketOrderOptions} for details.
|
|
238
299
|
*
|
|
@@ -246,7 +307,7 @@ ob.market({ side: 'buy' | 'sell', size: number })
|
|
|
246
307
|
|
|
247
308
|
For example:
|
|
248
309
|
|
|
249
|
-
```
|
|
310
|
+
```ts
|
|
250
311
|
ob.market({ side: 'sell', size: 6 })
|
|
251
312
|
|
|
252
313
|
asks: 110 -> 5 110 -> 5
|
|
@@ -260,7 +321,7 @@ partial - 1 order with price 80
|
|
|
260
321
|
quantityLeft - 0
|
|
261
322
|
```
|
|
262
323
|
|
|
263
|
-
```
|
|
324
|
+
```ts
|
|
264
325
|
ob.market({ side: 'buy', size: 10 })
|
|
265
326
|
|
|
266
327
|
asks: 110 -> 5
|
|
@@ -276,7 +337,7 @@ quantityLeft - 4
|
|
|
276
337
|
|
|
277
338
|
### Create Stop Limit Order
|
|
278
339
|
|
|
279
|
-
```
|
|
340
|
+
```ts
|
|
280
341
|
/**
|
|
281
342
|
* Create a stop limit order. See {@link StopLimitOrderOptions} for details.
|
|
282
343
|
*
|
|
@@ -289,12 +350,19 @@ quantityLeft - 4
|
|
|
289
350
|
* @param options.timeInForce - Time-in-force type supported are: GTC, FOK, IOC. Default is GTC
|
|
290
351
|
* @returns An object with the result of the processed order or an error. See {@link IProcessOrder} for the returned data structure
|
|
291
352
|
*/
|
|
292
|
-
ob.stopLimit({
|
|
353
|
+
ob.stopLimit({
|
|
354
|
+
side: 'buy' | 'sell',
|
|
355
|
+
id: string,
|
|
356
|
+
size: number,
|
|
357
|
+
price: number,
|
|
358
|
+
stopPrice: number,
|
|
359
|
+
timeInForce?: 'GTC' | 'FOK' | 'IOC'
|
|
360
|
+
})
|
|
293
361
|
```
|
|
294
362
|
|
|
295
363
|
### Create Stop Market Order
|
|
296
364
|
|
|
297
|
-
```
|
|
365
|
+
```ts
|
|
298
366
|
/**
|
|
299
367
|
* Create a stop market order. See {@link StopMarketOrderOptions} for details.
|
|
300
368
|
*
|
|
@@ -304,12 +372,16 @@ ob.stopLimit({ side: 'buy' | 'sell', id: string, size: number, price: number, st
|
|
|
304
372
|
* @param options.stopPrice - The price at which the order will be triggered.
|
|
305
373
|
* @returns An object with the result of the processed order or an error. See {@link IProcessOrder} for the returned data structure
|
|
306
374
|
*/
|
|
307
|
-
ob.stopMarket({
|
|
375
|
+
ob.stopMarket({
|
|
376
|
+
side: 'buy' | 'sell',
|
|
377
|
+
size: number,
|
|
378
|
+
stopPrice: number
|
|
379
|
+
})
|
|
308
380
|
```
|
|
309
381
|
|
|
310
382
|
### Create OCO (One-Cancels-the-Other) Order
|
|
311
383
|
|
|
312
|
-
```
|
|
384
|
+
```ts
|
|
313
385
|
/**
|
|
314
386
|
* Create an OCO (One-Cancels-the-Other) order.
|
|
315
387
|
* OCO order combines a `stop_limit` order and a `limit` order, where if stop price
|
|
@@ -333,12 +405,21 @@ ob.stopMarket({ side: 'buy' | 'sell', size: number, stopPrice: number })
|
|
|
333
405
|
* @param options.stopLimitTimeInForce - Time-in-force of the `stop_limit` order. Type supported are: GTC, FOK, IOC. Default is GTC
|
|
334
406
|
* @returns An object with the result of the processed order or an error. See {@link IProcessOrder} for the returned data structure
|
|
335
407
|
*/
|
|
336
|
-
ob.oco({
|
|
408
|
+
ob.oco({
|
|
409
|
+
side: 'buy' | 'sell',
|
|
410
|
+
id: string,
|
|
411
|
+
size: number,
|
|
412
|
+
price: number,
|
|
413
|
+
stopPrice: number,
|
|
414
|
+
stopLimitPrice: number,
|
|
415
|
+
timeInForce?: 'GTC' | 'FOK' | 'IOC',
|
|
416
|
+
stopLimitTimeInForce?: 'GTC' | 'FOK' | 'IOC'
|
|
417
|
+
})
|
|
337
418
|
```
|
|
338
419
|
|
|
339
420
|
### Modify an existing order
|
|
340
421
|
|
|
341
|
-
```
|
|
422
|
+
```ts
|
|
342
423
|
/**
|
|
343
424
|
* Modify an existing order with given ID. When an order is modified by price or quantity,
|
|
344
425
|
* it will be deemed as a new entry. Under the price-time-priority algorithm, orders are
|
|
@@ -354,7 +435,7 @@ ob.modify(orderID: string, { size: number, price: number })
|
|
|
354
435
|
|
|
355
436
|
For example:
|
|
356
437
|
|
|
357
|
-
```
|
|
438
|
+
```ts
|
|
358
439
|
ob.limit({ side: "sell", id: "uniqueID", size: 55, price: 100 })
|
|
359
440
|
|
|
360
441
|
asks: 110 -> 5 110 -> 5
|
|
@@ -385,7 +466,7 @@ bids: 90 -> 5 90 -> 5
|
|
|
385
466
|
|
|
386
467
|
### Cancel Order
|
|
387
468
|
|
|
388
|
-
```
|
|
469
|
+
```ts
|
|
389
470
|
/**
|
|
390
471
|
* Remove an existing order with given ID from the order book
|
|
391
472
|
*
|
|
@@ -397,7 +478,7 @@ ob.cancel(orderID: string)
|
|
|
397
478
|
|
|
398
479
|
For example:
|
|
399
480
|
|
|
400
|
-
```
|
|
481
|
+
```ts
|
|
401
482
|
ob.cancel("myUniqueID-Sell-1-with-100")
|
|
402
483
|
|
|
403
484
|
asks: 110 -> 5
|
|
@@ -407,6 +488,202 @@ bids: 90 -> 5 90 -> 5
|
|
|
407
488
|
80 -> 1 80 -> 1
|
|
408
489
|
```
|
|
409
490
|
|
|
491
|
+
## Self-Trade Prevention (STP)
|
|
492
|
+
|
|
493
|
+
> Inspired by [Binance's Self-Trade Prevention](https://developers.binance.com/docs/derivatives/usds-margined-futures/faq/stp-faq) — prevents orders from the same account from matching against each other.
|
|
494
|
+
|
|
495
|
+
### How it works
|
|
496
|
+
|
|
497
|
+
Each order can carry an `accountId` and a `stpMode`. When a taker order enters the book and would match against a maker order with the same `accountId`, the STP mode of the **taker order** determines what happens:
|
|
498
|
+
|
|
499
|
+
| Mode | Effect |
|
|
500
|
+
|------|--------|
|
|
501
|
+
| `NONE` | No prevention — orders match normally |
|
|
502
|
+
| `EXPIRE_MAKER` | The resting maker order(s) expire; the taker order continues |
|
|
503
|
+
| `EXPIRE_TAKER` | The taker order is rejected; the resting maker order(s) stay on the book |
|
|
504
|
+
| `EXPIRE_BOTH` | Both the taker and the matching maker order(s) expire |
|
|
505
|
+
|
|
506
|
+
The STP mode of the **taker** order always takes precedence — the mode stored on a resting maker order is ignored for STP purposes.
|
|
507
|
+
|
|
508
|
+
### API reference
|
|
509
|
+
|
|
510
|
+
Add `accountId` and `stpMode` to any order:
|
|
511
|
+
|
|
512
|
+
```ts
|
|
513
|
+
import { OrderBook, SelfTradePreventionMode, Side } from 'nodejs-order-book'
|
|
514
|
+
|
|
515
|
+
const ob = new OrderBook()
|
|
516
|
+
|
|
517
|
+
// Place a resting limit order from account "alice"
|
|
518
|
+
ob.limit({
|
|
519
|
+
side: Side.BUY,
|
|
520
|
+
id: 'maker-order',
|
|
521
|
+
size: 5,
|
|
522
|
+
price: 100,
|
|
523
|
+
accountId: 'alice',
|
|
524
|
+
})
|
|
525
|
+
|
|
526
|
+
// Taker from the same account with STP enabled
|
|
527
|
+
const result = ob.limit({
|
|
528
|
+
side: Side.SELL,
|
|
529
|
+
id: 'taker-order',
|
|
530
|
+
size: 3,
|
|
531
|
+
price: 90,
|
|
532
|
+
accountId: 'alice',
|
|
533
|
+
stpMode: SelfTradePreventionMode.EXPIRE_MAKER,
|
|
534
|
+
})
|
|
535
|
+
|
|
536
|
+
// Check which orders expired due to STP
|
|
537
|
+
console.log(result.stpExpired) // [{ id: 'maker-order', ... }]
|
|
538
|
+
```
|
|
539
|
+
|
|
540
|
+
### Response fields
|
|
541
|
+
|
|
542
|
+
When STP is triggered, the response (`IProcessOrder`) includes:
|
|
543
|
+
|
|
544
|
+
| Field | Type | Description |
|
|
545
|
+
|-------|------|-------------|
|
|
546
|
+
| `stpExpired` | `IOrder[] \| undefined` | Orders that were removed from the book due to STP |
|
|
547
|
+
| `err` | `OrderBookError \| null` | Error with `code: 1202` and `message: "Self-trade prevention triggered"` for `EXPIRE_TAKER` / `EXPIRE_BOTH` |
|
|
548
|
+
|
|
549
|
+
### Error code
|
|
550
|
+
|
|
551
|
+
STP rejections return error code `1202`:
|
|
552
|
+
|
|
553
|
+
```ts
|
|
554
|
+
import { ErrorCodes } from 'nodejs-order-book'
|
|
555
|
+
|
|
556
|
+
assert.equal(result.err?.code, ErrorCodes.STP_TRIGGERED)
|
|
557
|
+
// → 1202
|
|
558
|
+
assert.equal(result.err?.message, 'Self-trade prevention triggered')
|
|
559
|
+
```
|
|
560
|
+
|
|
561
|
+
### Scenarios
|
|
562
|
+
|
|
563
|
+
#### A) EXPIRE_MAKER — maker expires, taker continues
|
|
564
|
+
|
|
565
|
+
```
|
|
566
|
+
Maker BUY @ 100 qty: 5 account: "alice"
|
|
567
|
+
Maker BUY @ 90 qty: 5 account: "alice"
|
|
568
|
+
Taker SELL @ 90 qty: 3 account: "alice" mode: EXPIRE_MAKER
|
|
569
|
+
```
|
|
570
|
+
|
|
571
|
+
The two resting buy orders share the same account as the taker. With `EXPIRE_MAKER`, they are removed from the book and reported in `stpExpired[]`. The taker order (size 3) is placed on the book as a new maker.
|
|
572
|
+
|
|
573
|
+
```
|
|
574
|
+
stpExpired → [maker-buy-100, maker-buy-90]
|
|
575
|
+
err → null
|
|
576
|
+
```
|
|
577
|
+
|
|
578
|
+
#### B) EXPIRE_TAKER — taker expires, maker stays
|
|
579
|
+
|
|
580
|
+
```
|
|
581
|
+
Maker BUY @ 100 qty: 5 account: "alice"
|
|
582
|
+
Taker SELL @ 90 qty: 3 account: "alice" mode: EXPIRE_TAKER
|
|
583
|
+
```
|
|
584
|
+
|
|
585
|
+
The taker order is rejected immediately. The resting maker order remains untouched on the book.
|
|
586
|
+
|
|
587
|
+
```
|
|
588
|
+
stpExpired → undefined
|
|
589
|
+
err → { code: 1202, message: "Self-trade prevention triggered" }
|
|
590
|
+
```
|
|
591
|
+
|
|
592
|
+
#### C) EXPIRE_BOTH — both orders expire
|
|
593
|
+
|
|
594
|
+
```
|
|
595
|
+
Maker BUY @ 100 qty: 5 account: "alice"
|
|
596
|
+
Taker SELL @ 90 qty: 3 account: "alice" mode: EXPIRE_BOTH
|
|
597
|
+
```
|
|
598
|
+
|
|
599
|
+
The maker is removed from the book and the taker is rejected. Both sides expire.
|
|
600
|
+
|
|
601
|
+
```
|
|
602
|
+
stpExpired → [maker-buy-100]
|
|
603
|
+
err → { code: 1202, message: "Self-trade prevention triggered" }
|
|
604
|
+
```
|
|
605
|
+
|
|
606
|
+
#### D) Different accounts — normal matching (no STP)
|
|
607
|
+
|
|
608
|
+
```
|
|
609
|
+
Maker BUY @ 100 qty: 5 account: "alice"
|
|
610
|
+
Taker SELL @ 90 qty: 3 account: "bob" mode: EXPIRE_MAKER
|
|
611
|
+
```
|
|
612
|
+
|
|
613
|
+
The accounts differ, so STP does **not** trigger. The orders match normally.
|
|
614
|
+
|
|
615
|
+
```
|
|
616
|
+
done → [filled trade summary]
|
|
617
|
+
stpExpired → undefined
|
|
618
|
+
```
|
|
619
|
+
|
|
620
|
+
#### E) Mode NONE — no prevention
|
|
621
|
+
|
|
622
|
+
```
|
|
623
|
+
Maker BUY @ 100 qty: 5 account: "alice"
|
|
624
|
+
Taker SELL @ 90 qty: 3 account: "alice" mode: NONE
|
|
625
|
+
```
|
|
626
|
+
|
|
627
|
+
Even though both orders are from the same account, `NONE` mode allows the match.
|
|
628
|
+
|
|
629
|
+
```
|
|
630
|
+
done → [filled trade summary]
|
|
631
|
+
stpExpired → undefined
|
|
632
|
+
```
|
|
633
|
+
|
|
634
|
+
#### F) Market order with EXPIRE_MAKER
|
|
635
|
+
|
|
636
|
+
```
|
|
637
|
+
Maker BUY @ 100 qty: 5 account: "alice"
|
|
638
|
+
Taker SELL (market) qty: 3 account: "alice" mode: EXPIRE_MAKER
|
|
639
|
+
```
|
|
640
|
+
|
|
641
|
+
The resting maker is expired via STP. The market order has no remaining liquidity, so it also expires.
|
|
642
|
+
|
|
643
|
+
```
|
|
644
|
+
stpExpired → [maker-buy-100]
|
|
645
|
+
err → null
|
|
646
|
+
```
|
|
647
|
+
|
|
648
|
+
#### G) Mixed accounts at the same price level
|
|
649
|
+
|
|
650
|
+
```
|
|
651
|
+
Maker "alice" BUY @ 100 qty: 5
|
|
652
|
+
Maker "bob" BUY @ 100 qty: 5
|
|
653
|
+
Taker "alice" SELL @ 90 qty: 8 mode: EXPIRE_MAKER
|
|
654
|
+
```
|
|
655
|
+
|
|
656
|
+
At price level 100, alice's maker is expired (`stpExpired`), while bob's maker matches normally (`done`). The remaining taker quantity (3) rests on the book.
|
|
657
|
+
|
|
658
|
+
```
|
|
659
|
+
stpExpired → [maker-alice-100]
|
|
660
|
+
done → [maker-bob-100]
|
|
661
|
+
```
|
|
662
|
+
|
|
663
|
+
#### H) STP carries through triggered stop orders
|
|
664
|
+
|
|
665
|
+
Stop orders preserve the `stpMode` they were created with. When a stop order is triggered and becomes a taker, its STP mode is applied at match time.
|
|
666
|
+
|
|
667
|
+
```ts
|
|
668
|
+
ob.createOrder({
|
|
669
|
+
type: OrderType.STOP_LIMIT,
|
|
670
|
+
side: Side.BUY,
|
|
671
|
+
size: 3,
|
|
672
|
+
price: 110,
|
|
673
|
+
stopPrice: 108,
|
|
674
|
+
accountId: 'alice',
|
|
675
|
+
stpMode: SelfTradePreventionMode.EXPIRE_MAKER,
|
|
676
|
+
})
|
|
677
|
+
```
|
|
678
|
+
|
|
679
|
+
### Important notes
|
|
680
|
+
|
|
681
|
+
- STP is evaluated using the **taker order's** mode, regardless of what mode the resting maker orders carry.
|
|
682
|
+
- If no `accountId` is specified on either side, STP is **not** triggered (backward compatible).
|
|
683
|
+
- If no `stpMode` is specified, it defaults to `NONE` (no prevention).
|
|
684
|
+
- Stop market and stop limit orders preserve the `stpMode` and apply it when triggered.
|
|
685
|
+
- Modify operations reset `stpMode` to `NONE`.
|
|
686
|
+
|
|
410
687
|
## Order Book Options
|
|
411
688
|
|
|
412
689
|
The orderbook can be initialized with the following options by passing them to the constructor:
|
|
@@ -425,7 +702,7 @@ After taking the snapshot, you can safely remove all logs preceding the `lastOp`
|
|
|
425
702
|
|
|
426
703
|
**Note**: The snapshot of the order book returns an object containing an `array` of `bids` and `asks`, which in turn are arrays of order objects. If the snapshot is saved to the database as a `string`, make sure to pass the snapshot in its original format when initializing the order book. For example, you can achieve this by using `JSON.parse` to convert the string back into its original object form.
|
|
427
704
|
|
|
428
|
-
```
|
|
705
|
+
```ts
|
|
429
706
|
const ob = new OrderBook({ enableJournaling: true})
|
|
430
707
|
|
|
431
708
|
// after every order save the log to the database
|
|
@@ -449,7 +726,7 @@ const ob = new OrderBook({ snapshot: JSON.parse(snapshot), journal: log, enableJ
|
|
|
449
726
|
|
|
450
727
|
### Journal Logs
|
|
451
728
|
The `journal` option expects an array of journal logs that you can get by setting `enableJournaling` to true. When the journal is provided, the order book will replay all the operations, bringing the order book to the same state as the last log.
|
|
452
|
-
```
|
|
729
|
+
```ts
|
|
453
730
|
// Assuming 'logs' is an array of log entries retrieved from the database
|
|
454
731
|
|
|
455
732
|
const logs = await getLogs()
|
|
@@ -459,7 +736,7 @@ By combining snapshots with journaling, you can effectively restore and audit th
|
|
|
459
736
|
|
|
460
737
|
### Enable Journaling
|
|
461
738
|
`enabledJournaling` is a configuration setting that determines whether journaling is enabled or disabled. When enabled, the property `log` will be added to the body of the response for each operation. The logs must be saved to the database and can then be used when a new instance of the order book is instantiated.
|
|
462
|
-
```
|
|
739
|
+
```ts
|
|
463
740
|
const ob = new OrderBook({ enableJournaling: true }) // false by default
|
|
464
741
|
|
|
465
742
|
// after every order save the log to the database
|
|
@@ -473,7 +750,7 @@ await saveLog(order.log)
|
|
|
473
750
|
|
|
474
751
|
Build production (distribution) files in your dist folder:
|
|
475
752
|
|
|
476
|
-
```
|
|
753
|
+
```
|
|
477
754
|
npm run build
|
|
478
755
|
```
|
|
479
756
|
|
|
@@ -481,7 +758,7 @@ npm run build
|
|
|
481
758
|
|
|
482
759
|
To run all the unit-test
|
|
483
760
|
|
|
484
|
-
```
|
|
761
|
+
```
|
|
485
762
|
npm run test
|
|
486
763
|
```
|
|
487
764
|
|
|
@@ -489,7 +766,7 @@ npm run test
|
|
|
489
766
|
|
|
490
767
|
Run testing coverage
|
|
491
768
|
|
|
492
|
-
```
|
|
769
|
+
```
|
|
493
770
|
npm run test:cov
|
|
494
771
|
```
|
|
495
772
|
|
|
@@ -497,7 +774,7 @@ npm run test:cov
|
|
|
497
774
|
|
|
498
775
|
Before running benchmark, make sure to have built the source code with `npm run build` first
|
|
499
776
|
|
|
500
|
-
```
|
|
777
|
+
```
|
|
501
778
|
npm run bench
|
|
502
779
|
```
|
|
503
780
|
|