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 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
- ```sh
68
+ ```
67
69
  npm install nodejs-order-book
68
70
  ```
69
71
 
70
72
  Install with yarn:
71
73
 
72
- ```sh
74
+ ```
73
75
  yarn add nodejs-order-book
74
76
  ```
75
77
 
76
78
  Install with pnpm:
77
79
 
78
- ```sh
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
- ```js
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
- ```js
95
- ob.createOrder({ type: 'limit' | 'market', side: 'buy' | 'sell', size: number, price?: number, id?: string, postOnly?: boolean, timeInForce?: 'GTC' | 'FOK' | 'IOC' })
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({ id: string, side: 'buy' | 'sell', size: number, price: number, postOnly?: boolean, timeInForce?: 'GTC' | 'FOK' | 'IOC' })
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, { side: 'buy' | 'sell', size: number, price: number })
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
- ```js
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
- ```js
177
+ ```ts
157
178
  // Create limit order
158
- ob.createOrder({ type: 'limit', side: 'buy' | 'sell', size: number, price: number, id: string, postOnly?: boolean, timeInForce?: 'GTC' | 'FOK' | 'IOC' })
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({ type: 'market', side: 'buy' | 'sell', size: number })
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({ type: 'stop_limit', side: 'buy' | 'sell', size: number, price: number, id: string, stopPrice: number, timeInForce?: 'GTC' | 'FOK' | 'IOC' })
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({ type: 'stop_market', side: 'buy' | 'sell', size: number, stopPrice: number })
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({ type: 'oco', side: 'buy' | 'sell', size: number, stopPrice: number, stopLimitPrice: number, timeInForce?: 'GTC' | 'FOK' | 'IOC', stopLimitTimeInForce?: 'GTC' | 'FOK' | 'IOC' })
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
- ```js
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({ side: 'buy' | 'sell', id: string, size: number, price: number, postOnly?: boolean, timeInForce?: 'GTC' | 'FOK' | 'IOC' })
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
- ```js
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
- ```js
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({ side: 'buy' | 'sell', id: string, size: number, price: number, stopPrice: number, timeInForce?: 'GTC' | 'FOK' | 'IOC' })
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
- ```js
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({ side: 'buy' | 'sell', size: number, stopPrice: number })
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
- ```js
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({ side: 'buy' | 'sell', id: string, size: number, price: number, stopPrice: number, stopLimitPrice: number, timeInForce?: 'GTC' | 'FOK' | 'IOC', stopLimitTimeInForce?: 'GTC' | 'FOK' | 'IOC' })
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
- ```js
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
- ```js
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
- ```js
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
- ```js
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
- ```js
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
- ```sh
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
- ```sh
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
- ```sh
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
- ```sh
777
+ ```
501
778
  npm run bench
502
779
  ```
503
780