nodejs-order-book 8.0.1 → 9.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 CHANGED
@@ -9,20 +9,50 @@
9
9
 
10
10
  # Node.js Order Book
11
11
 
12
- Ultra-fast Node.js Order Book written in TypeScript for high-frequency trading (HFT) :rocket::rocket:
13
-
12
+ <p align="center">
13
+ Ultra-fast Node.js Order Book written in TypeScript </br> for high-frequency trading (HFT) :rocket::rocket: </br></br>
14
14
  :star: Star me on GitHub — it motivates me a lot!
15
+ </p>
16
+
17
+ ## Table of Contents
18
+
19
+ - [Features](#features)
20
+ - [Installation](#installation)
21
+ - [Usage](#usage)
22
+ - [Experimental Conditional Orders](#conditional-orders-)
23
+ - [About Primary Functions](#about-primary-functions)
24
+ - [Create order `createOrder()`](#create-order)
25
+ - [Create Limit order `limit()`](#create-limit-order)
26
+ - [Create Market order `market()`](#create-market-order)
27
+ - [Create Stop Limit order `stopLimit()`](#create-stop-limit-order)
28
+ - [Create Stop Market order `stopMarket()`](#create-stop-market-order)
29
+ - [Create OCO (One-Cancels-the-Other) order `oco()`](#create-oco-one-cancels-the-other-order)
30
+ - [Modify an existing order `modifiy()`](#modify-an-existing-order)
31
+ - [Cancel order `cancel()`](#cancel-order)
32
+ - [Order Book Options](#order-book-options)
33
+ - [Snapshot](#snapshot)
34
+ - [Journal Logs](#journal-logs)
35
+ - [Enable Journaling](#enable-journaling)
36
+ - [Development](#development)
37
+ - [Build](#build)
38
+ - [Testing](#testing)
39
+ - [Coverage](#coverage)
40
+ - [Benchmarking](#benchmarking)
41
+ - [Contributing](#contributing)
42
+ - [Donation](#donation)
43
+ - [License](#license)
15
44
 
16
45
  ## Features
46
+ > Initially ported from [Go orderbook](https://github.com/i25959341/orderbook), this order book has been enhanced with new features
17
47
 
18
48
  - Standard price-time priority
19
49
  - Supports both market and limit orders
20
- - Supports `post-only` limit order
21
- - Supports conditional orders [**Stop Limit, Stop Market and OCO**](#conditional-orders-) <img src="https://img.shields.io/badge/Experimental-blue" alt="Experimental">
22
- - Supports time in force GTC, FOK and IOC
50
+ - Supports `post-only` limit order <img src="https://img.shields.io/badge/New-green" alt="New">
51
+ - Supports conditional orders [**Stop Limit, Stop Market and OCO**](#conditional-orders-) <img src="https://img.shields.io/badge/New-green" alt="New"> <img src="https://img.shields.io/badge/Experimental-blue" alt="Experimental">
52
+ - Supports time in force GTC, FOK and IOC <img src="https://img.shields.io/badge/New-green" alt="New">
23
53
  - Supports order cancelling
24
- - Supports order price and/or size updating
25
- - Snapshot and journaling functionalities for restoring the order book during server startup
54
+ - Supports order price and/or size updating <img src="https://img.shields.io/badge/New-green" alt="New">
55
+ - Snapshot and journaling functionalities for restoring the order book during server startup <img src="https://img.shields.io/badge/New-green" alt="New">
26
56
  - **High performance (above 300k trades per second)**
27
57
 
28
58
  **Machine:** ASUS ExpertBook, 11th Gen Intel(R) Core(TM) i7-1165G7, 2.80Ghz, 16GB RAM, Node.js v18.4.0.
@@ -348,7 +378,7 @@ bids: 90 -> 5 90 -> 5
348
378
  80 -> 1 80 -> 1
349
379
  ```
350
380
 
351
- ## Options
381
+ ## Order Book Options
352
382
 
353
383
  The orderbook can be initialized with the following options by passing them to the constructor:
354
384
 
@@ -363,6 +393,8 @@ A `snapshot` represents the state of the order book at a specific point in time.
363
393
  Snapshots are crucial for restoring the order book to a previous state. The orderbook can restore from a snapshot before processing any journal logs, ensuring consistency and accuracy.
364
394
  After taking the snapshot, you can safely remove all logs preceding the `lastOp` id.
365
395
 
396
+ **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.
397
+
366
398
  ```js
367
399
  const ob = new OrderBook({ enableJournaling: true})
368
400
 
package/dist/cjs/order.js CHANGED
@@ -20,6 +20,7 @@ exports.OrderFactory = exports.StopLimitOrder = exports.StopMarketOrder = export
20
20
  var node_crypto_1 = require("node:crypto");
21
21
  var errors_1 = require("./errors");
22
22
  var types_1 = require("./types");
23
+ var utils_1 = require("./utils");
23
24
  var BaseOrder = /** @class */ (function () {
24
25
  function BaseOrder(options) {
25
26
  var _a, _b;
@@ -78,7 +79,7 @@ var LimitOrder = /** @class */ (function (_super) {
78
79
  _this.toString = function () {
79
80
  return "".concat(_this._id, ":\n type: ").concat(_this.type, "\n side: ").concat(_this._side, "\n size: ").concat(_this._size, "\n origSize: ").concat(_this._origSize, "\n price: ").concat(_this._price, "\n time: ").concat(_this._time, "\n timeInForce: ").concat(_this._timeInForce, "\n makerQty: ").concat(_this._makerQty, "\n takerQty: ").concat(_this._takerQty);
80
81
  };
81
- _this.toJSON = function () { return JSON.stringify(_this.toObject()); };
82
+ _this.toJSON = function () { return (0, utils_1.safeStringify)(_this.toObject()); };
82
83
  _this.toObject = function () { return ({
83
84
  id: _this._id,
84
85
  type: _this.type,
@@ -178,7 +179,7 @@ var StopMarketOrder = /** @class */ (function (_super) {
178
179
  _this.toString = function () {
179
180
  return "".concat(_this._id, ":\n type: ").concat(_this.type, "\n side: ").concat(_this._side, "\n size: ").concat(_this._size, "\n stopPrice: ").concat(_this._stopPrice, "\n time: ").concat(_this._time);
180
181
  };
181
- _this.toJSON = function () { return JSON.stringify(_this.toObject()); };
182
+ _this.toJSON = function () { return (0, utils_1.safeStringify)(_this.toObject()); };
182
183
  _this.toObject = function () { return ({
183
184
  id: _this._id,
184
185
  type: _this.type,
@@ -216,9 +217,9 @@ var StopLimitOrder = /** @class */ (function (_super) {
216
217
  var _a;
217
218
  var _this = _super.call(this, options) || this;
218
219
  _this.toString = function () {
219
- return "".concat(_this._id, ":\n type: ").concat(_this.type, "\n side: ").concat(_this._side, "\n size: ").concat(_this._size, "\n price: ").concat(_this._price, "\n stopPrice: ").concat(_this._stopPrice, "\n timeInForce: ").concat(_this._timeInForce, "\n time: ").concat(_this._time);
220
+ return "".concat(_this._id, ":\n type: ").concat(_this.type, "\n side: ").concat(_this._side, "\n size: ").concat(_this._size, "\n price: ").concat(_this._price, "\n stopPrice: ").concat(_this._stopPrice, "\n\tisOCO: ").concat(_this.isOCO, "\n timeInForce: ").concat(_this._timeInForce, "\n time: ").concat(_this._time);
220
221
  };
221
- _this.toJSON = function () { return JSON.stringify(_this.toObject()); };
222
+ _this.toJSON = function () { return (0, utils_1.safeStringify)(_this.toObject()); };
222
223
  _this.toObject = function () { return ({
223
224
  id: _this._id,
224
225
  type: _this.type,
@@ -226,6 +227,7 @@ var StopLimitOrder = /** @class */ (function (_super) {
226
227
  size: _this._size,
227
228
  price: _this._price,
228
229
  stopPrice: _this._stopPrice,
230
+ isOCO: _this.isOCO,
229
231
  timeInForce: _this._timeInForce,
230
232
  time: _this._time,
231
233
  }); };
@@ -47,7 +47,16 @@ var OrderBook = /** @class */ (function () {
47
47
  /* node:coverage ignore next 4 - We don't need test for this */
48
48
  if (!_this.experimentalConditionalOrders)
49
49
  throw new Error("In order to use conditional orders you need to instantiate the order book with the `experimentalConditionalOrders` option set to true");
50
- return _this._stopMarket(options);
50
+ var response = _this._stopMarket(options);
51
+ if (_this.enableJournaling && response.err === null) {
52
+ response.log = {
53
+ opId: ++_this._lastOp,
54
+ ts: Date.now(),
55
+ op: "sm",
56
+ o: options,
57
+ };
58
+ }
59
+ return response;
51
60
  };
52
61
  /**
53
62
  * Create a stop limit order. See {@link StopLimitOrderOptions} for details.
@@ -65,7 +74,16 @@ var OrderBook = /** @class */ (function () {
65
74
  /* node:coverage ignore next 4 - We don't need test for this */
66
75
  if (!_this.experimentalConditionalOrders)
67
76
  throw new Error("In order to use conditional orders you need to instantiate the order book with the `experimentalConditionalOrders` option set to true");
68
- return _this._stopLimit(options);
77
+ var response = _this._stopLimit(options);
78
+ if (_this.enableJournaling && response.err === null) {
79
+ response.log = {
80
+ opId: ++_this._lastOp,
81
+ ts: Date.now(),
82
+ op: "sl",
83
+ o: options,
84
+ };
85
+ }
86
+ return response;
69
87
  };
70
88
  /**
71
89
  * Create an OCO (One-Cancels-the-Other) order.
@@ -94,7 +112,16 @@ var OrderBook = /** @class */ (function () {
94
112
  /* node:coverage ignore next 4 - We don't need test for this */
95
113
  if (!_this.experimentalConditionalOrders)
96
114
  throw new Error("In order to use conditional orders you need to instantiate the order book with the `experimentalConditionalOrders` option set to true");
97
- return _this._oco(options);
115
+ var response = _this._oco(options);
116
+ if (_this.enableJournaling && response.err === null) {
117
+ response.log = {
118
+ opId: ++_this._lastOp,
119
+ ts: Date.now(),
120
+ op: "oco",
121
+ o: options,
122
+ };
123
+ }
124
+ return response;
98
125
  };
99
126
  /**
100
127
  * Modify an existing order with given ID. When an order is modified by price or quantity,
@@ -163,7 +190,8 @@ var OrderBook = /** @class */ (function () {
163
190
  * @returns The order if exists or `undefined`
164
191
  */
165
192
  this.order = function (orderID) {
166
- return _this.orders[orderID];
193
+ var _a;
194
+ return (_a = _this.orders[orderID]) === null || _a === void 0 ? void 0 : _a.toObject();
167
195
  };
168
196
  // Returns price levels and volume at price level
169
197
  this.depth = function () {
@@ -219,16 +247,16 @@ var OrderBook = /** @class */ (function () {
219
247
  var bids = [];
220
248
  var asks = [];
221
249
  _this.bids.priceTree().forEach(function (price, orders) {
222
- bids.push({ price: price, orders: orders.toArray() });
250
+ bids.push({ price: price, orders: orders.toArray().map(function (o) { return o.toObject(); }) });
223
251
  });
224
252
  _this.asks.priceTree().forEach(function (price, orders) {
225
- asks.push({ price: price, orders: orders.toArray() });
253
+ asks.push({ price: price, orders: orders.toArray().map(function (o) { return o.toObject(); }) });
226
254
  });
227
255
  return { bids: bids, asks: asks, ts: Date.now(), lastOp: _this._lastOp };
228
256
  };
229
257
  this._market = function (options, incomingResponse) {
230
258
  var response = incomingResponse !== null && incomingResponse !== void 0 ? incomingResponse : _this.validateMarketOrder(options);
231
- if (response.err != null)
259
+ if (response.err !== null)
232
260
  return response;
233
261
  var quantityToTrade = options.size;
234
262
  var iter;
@@ -253,41 +281,19 @@ var OrderBook = /** @class */ (function () {
253
281
  }
254
282
  response.quantityLeft = quantityToTrade;
255
283
  _this.executeConditionalOrder(options.side, priceBefore, response);
256
- if (_this.enableJournaling) {
257
- response.log = {
258
- opId: ++_this._lastOp,
259
- ts: Date.now(),
260
- op: "m",
261
- o: { side: options.side, size: options.size },
262
- };
263
- }
264
284
  return response;
265
285
  };
266
286
  this._limit = function (options, incomingResponse) {
267
287
  var _a, _b;
268
288
  var response = incomingResponse !== null && incomingResponse !== void 0 ? incomingResponse : _this.validateLimitOrder(options);
269
- if (response.err != null)
289
+ if (response.err !== null)
270
290
  return response;
271
- var order = _this.createLimitOrder(response, options.side, options.id, options.size, options.price, (_a = options.postOnly) !== null && _a !== void 0 ? _a : false, (_b = options.timeInForce) !== null && _b !== void 0 ? _b : types_1.TimeInForce.GTC, options.ocoStopPrice);
272
- if (_this.enableJournaling && order != null) {
273
- response.log = {
274
- opId: ++_this._lastOp,
275
- ts: Date.now(),
276
- op: "l",
277
- o: {
278
- side: order.side,
279
- id: order.id,
280
- size: order.size,
281
- price: order.price,
282
- timeInForce: order.timeInForce,
283
- },
284
- };
285
- }
291
+ _this.createLimitOrder(response, options.side, options.id, options.size, options.price, (_a = options.postOnly) !== null && _a !== void 0 ? _a : false, (_b = options.timeInForce) !== null && _b !== void 0 ? _b : types_1.TimeInForce.GTC, options.ocoStopPrice);
286
292
  return response;
287
293
  };
288
294
  this._stopMarket = function (options) {
289
295
  var response = _this.validateMarketOrder(options);
290
- if (response.err != null)
296
+ if (response.err !== null)
291
297
  return response;
292
298
  var stopMarket = order_1.OrderFactory.createOrder(__assign(__assign({}, options), { type: types_1.OrderType.STOP_MARKET }));
293
299
  return _this._stopOrder(stopMarket, response);
@@ -295,7 +301,7 @@ var OrderBook = /** @class */ (function () {
295
301
  this._stopLimit = function (options) {
296
302
  var _a;
297
303
  var response = _this.validateLimitOrder(options);
298
- if (response.err != null)
304
+ if (response.err !== null)
299
305
  return response;
300
306
  var stopLimit = order_1.OrderFactory.createOrder(__assign(__assign({}, options), { type: types_1.OrderType.STOP_LIMIT, timeInForce: (_a = options.timeInForce) !== null && _a !== void 0 ? _a : types_1.TimeInForce.GTC }));
301
307
  return _this._stopOrder(stopLimit, response);
@@ -304,7 +310,7 @@ var OrderBook = /** @class */ (function () {
304
310
  var _a;
305
311
  var response = _this.validateLimitOrder(options);
306
312
  /* node:coverage ignore next - Already validated with limit test */
307
- if (response.err != null)
313
+ if (response.err !== null)
308
314
  return response;
309
315
  if (_this.validateOCOOrder(options)) {
310
316
  // We use the same ID for Stop Limit and Limit Order, since
@@ -318,7 +324,7 @@ var OrderBook = /** @class */ (function () {
318
324
  ocoStopPrice: options.stopPrice,
319
325
  }, response);
320
326
  /* node:coverage ignore next - Already validated with limit test */
321
- if (response.err != null)
327
+ if (response.err !== null)
322
328
  return response;
323
329
  var stopLimit = order_1.OrderFactory.createOrder({
324
330
  type: types_1.OrderType.STOP_LIMIT,
@@ -331,7 +337,7 @@ var OrderBook = /** @class */ (function () {
331
337
  isOCO: true,
332
338
  });
333
339
  _this.stopBook.add(stopLimit);
334
- response.done.push(stopLimit);
340
+ response.done.push(stopLimit.toObject());
335
341
  }
336
342
  else {
337
343
  response.err = (0, errors_1.CustomError)(errors_1.ERROR.INVALID_CONDITIONAL_ORDER);
@@ -341,7 +347,7 @@ var OrderBook = /** @class */ (function () {
341
347
  this._stopOrder = function (stopOrder, response) {
342
348
  if (_this.stopBook.validConditionalOrder(_this._marketPrice, stopOrder)) {
343
349
  _this.stopBook.add(stopOrder);
344
- response.done.push(stopOrder);
350
+ response.done.push(stopOrder.toObject());
345
351
  }
346
352
  else {
347
353
  response.err = (0, errors_1.CustomError)(errors_1.ERROR.INVALID_CONDITIONAL_ORDER);
@@ -354,16 +360,18 @@ var OrderBook = /** @class */ (function () {
354
360
  var level = _a[_i];
355
361
  for (var _b = 0, _c = level.orders; _b < _c.length; _b++) {
356
362
  var order = _c[_b];
357
- _this.orders[order.id] = order;
358
- _this.bids.append(order);
363
+ var newOrder = order_1.OrderFactory.createOrder(order);
364
+ _this.orders[newOrder.id] = newOrder;
365
+ _this.bids.append(newOrder);
359
366
  }
360
367
  }
361
368
  for (var _d = 0, _e = snapshot.asks; _d < _e.length; _d++) {
362
369
  var level = _e[_d];
363
370
  for (var _f = 0, _g = level.orders; _f < _g.length; _f++) {
364
371
  var order = _g[_f];
365
- _this.orders[order.id] = order;
366
- _this.asks.append(order);
372
+ var newOrder = order_1.OrderFactory.createOrder(order);
373
+ _this.orders[newOrder.id] = newOrder;
374
+ _this.asks.append(newOrder);
367
375
  }
368
376
  }
369
377
  };
@@ -374,6 +382,7 @@ var OrderBook = /** @class */ (function () {
374
382
  * @returns The removed order if exists or `undefined`
375
383
  */
376
384
  this._cancelOrder = function (orderID, internalDeletion) {
385
+ var _a, _b;
377
386
  if (internalDeletion === void 0) { internalDeletion = false; }
378
387
  var order = _this.orders[orderID];
379
388
  if (order === undefined)
@@ -381,11 +390,12 @@ var OrderBook = /** @class */ (function () {
381
390
  delete _this.orders[orderID];
382
391
  var side = order.side === types_1.Side.BUY ? _this.bids : _this.asks;
383
392
  var response = {
384
- order: side.remove(order),
393
+ order: (_a = side.remove(order)) === null || _a === void 0 ? void 0 : _a.toObject(),
385
394
  };
386
395
  // Delete OCO Order only when the delete request comes from user
387
396
  if (!internalDeletion && order.ocoStopPrice !== undefined) {
388
- response.stopOrder = _this.stopBook.remove(order.side, orderID, order.ocoStopPrice);
397
+ response.stopOrder = (_b = _this.stopBook
398
+ .remove(order.side, orderID, order.ocoStopPrice)) === null || _b === void 0 ? void 0 : _b.toObject();
389
399
  }
390
400
  if (_this.enableJournaling) {
391
401
  response.log = {
@@ -458,7 +468,7 @@ var OrderBook = /** @class */ (function () {
458
468
  order = order_1.OrderFactory.createOrder(__assign({ type: types_1.OrderType.LIMIT, id: orderID, side: side, size: quantityToTrade, origSize: size, price: price, time: Date.now(), timeInForce: timeInForce, postOnly: postOnly, takerQty: takerQty, makerQty: makerQty }, (ocoStopPrice !== undefined ? { ocoStopPrice: ocoStopPrice } : {})));
459
469
  if (response.done.length > 0) {
460
470
  response.partialQuantityProcessed = size - quantityToTrade;
461
- response.partial = order;
471
+ response.partial = order.toObject();
462
472
  }
463
473
  _this.orders[orderID] = sideToAdd.append(order);
464
474
  }
@@ -487,7 +497,7 @@ var OrderBook = /** @class */ (function () {
487
497
  takerQty: takerQty,
488
498
  makerQty: makerQty,
489
499
  });
490
- response.done.push(order);
500
+ response.done.push(order.toObject());
491
501
  }
492
502
  // If IOC order was not matched completely remove from the order book
493
503
  if (timeInForce === types_1.TimeInForce.IOC && response.quantityLeft > 0) {
@@ -533,7 +543,7 @@ var OrderBook = /** @class */ (function () {
533
543
  timeInForce: stopOrder.timeInForce,
534
544
  }, response);
535
545
  }
536
- response.activated.push(stopOrder);
546
+ response.activated.push(stopOrder.toObject());
537
547
  });
538
548
  }
539
549
  };
@@ -546,21 +556,48 @@ var OrderBook = /** @class */ (function () {
546
556
  if (side == null || size == null) {
547
557
  throw (0, errors_1.CustomError)(errors_1.ERROR.INVALID_JOURNAL_LOG);
548
558
  }
549
- _this.market({ side: side, size: size });
559
+ _this.market(log.o);
550
560
  break;
551
561
  }
552
562
  case "l": {
553
- var _b = log.o, side = _b.side, id = _b.id, size = _b.size, price = _b.price, timeInForce = _b.timeInForce;
563
+ var _b = log.o, side = _b.side, id = _b.id, size = _b.size, price = _b.price;
554
564
  if (side == null || id == null || size == null || price == null) {
555
565
  throw (0, errors_1.CustomError)(errors_1.ERROR.INVALID_JOURNAL_LOG);
556
566
  }
557
- _this.limit({
558
- side: side,
559
- id: id,
560
- size: size,
561
- price: price,
562
- timeInForce: timeInForce,
563
- });
567
+ _this.limit(log.o);
568
+ break;
569
+ }
570
+ case "sm": {
571
+ var _c = log.o, side = _c.side, size = _c.size, stopPrice = _c.stopPrice;
572
+ if (side == null || size == null || stopPrice == null) {
573
+ throw (0, errors_1.CustomError)(errors_1.ERROR.INVALID_JOURNAL_LOG);
574
+ }
575
+ _this.stopMarket(log.o);
576
+ break;
577
+ }
578
+ case "sl": {
579
+ var _d = log.o, side = _d.side, id = _d.id, size = _d.size, price = _d.price, stopPrice = _d.stopPrice;
580
+ if (side == null ||
581
+ id == null ||
582
+ size == null ||
583
+ price == null ||
584
+ stopPrice == null) {
585
+ throw (0, errors_1.CustomError)(errors_1.ERROR.INVALID_JOURNAL_LOG);
586
+ }
587
+ _this.stopLimit(log.o);
588
+ break;
589
+ }
590
+ case "oco": {
591
+ var _e = log.o, side = _e.side, id = _e.id, size = _e.size, price = _e.price, stopPrice = _e.stopPrice, stopLimitPrice = _e.stopLimitPrice;
592
+ if (side == null ||
593
+ id == null ||
594
+ size == null ||
595
+ price == null ||
596
+ stopPrice == null ||
597
+ stopLimitPrice == null) {
598
+ throw (0, errors_1.CustomError)(errors_1.ERROR.INVALID_JOURNAL_LOG);
599
+ }
600
+ _this.oco(log.o);
564
601
  break;
565
602
  }
566
603
  case "d":
@@ -618,10 +655,11 @@ var OrderBook = /** @class */ (function () {
618
655
  var headOrder = orderQueue.head();
619
656
  if (headOrder !== undefined) {
620
657
  if (response.quantityLeft < headOrder.size) {
621
- response.partial = order_1.OrderFactory.createOrder(__assign(__assign({}, headOrder.toObject()), { size: headOrder.size - response.quantityLeft }));
622
- _this.orders[headOrder.id] = response.partial;
658
+ var partial = order_1.OrderFactory.createOrder(__assign(__assign({}, headOrder.toObject()), { size: headOrder.size - response.quantityLeft }));
659
+ response.partial = partial.toObject();
660
+ _this.orders[headOrder.id] = partial;
623
661
  response.partialQuantityProcessed = response.quantityLeft;
624
- orderQueue.update(headOrder, response.partial);
662
+ orderQueue.update(headOrder, partial);
625
663
  response.quantityLeft = 0;
626
664
  }
627
665
  else {
@@ -751,40 +789,23 @@ var OrderBook = /** @class */ (function () {
751
789
  enumerable: false,
752
790
  configurable: true
753
791
  });
754
- OrderBook.prototype.createOrder = function (typeOrOptions, side, size, price, orderID, timeInForce, stopPrice, stopLimitPrice, stopLimitTimeInForce, postOnly) {
755
- if (timeInForce === void 0) { timeInForce = types_1.TimeInForce.GTC; }
756
- if (stopLimitTimeInForce === void 0) { stopLimitTimeInForce = types_1.TimeInForce.GTC; }
757
- var options;
758
- // We don't want to test the deprecated signature.
759
- /* node:coverage disable */
760
- if (typeof typeOrOptions === "string" &&
761
- side !== undefined &&
762
- size !== undefined) {
763
- options = {
764
- type: typeOrOptions,
765
- side: side,
766
- size: size,
767
- // @ts-expect-error
768
- price: price,
769
- id: orderID,
770
- timeInForce: timeInForce,
771
- // @ts-expect-error
772
- stopPrice: stopPrice,
773
- // @ts-expect-error
774
- stopLimitPrice: stopLimitPrice,
775
- stopLimitTimeInForce: stopLimitTimeInForce,
776
- postOnly: postOnly,
777
- };
778
- /* node:coverage enable */
779
- }
780
- else if (typeof typeOrOptions === "object") {
781
- options = typeOrOptions;
782
- /* node:coverage disable */
783
- }
784
- else {
785
- throw new Error("Invalid arguments.");
786
- }
787
- /* node:coverage enable */
792
+ /**
793
+ * Create new order. See {@link CreateOrderOptions} for details.
794
+ *
795
+ * @param options
796
+ * @param options.type - `limit` | `market` | 'stop_limit' | 'stop_market' | 'oco'
797
+ * @param options.side - `sell` or `buy`
798
+ * @param options.size - How much of currency you want to trade in units of base currency
799
+ * @param options.price - The price at which the order is to be fullfilled, in units of the quote currency. Param only for limit order
800
+ * @param options.orderID - Unique order ID. Param only for limit order
801
+ * @param options.postOnly - Can be used with 'limit' order and when it's `true` the order will be rejected if immediately matches and trades as a taker. Default is `false`
802
+ * @param options.stopPrice - The price at which the order will be triggered. Used with `stop_limit` and `stop_market` order.
803
+ * @param options.stopLimitPrice - The price at which the order will be triggered. Used with `stop_limit` and `stop_market` order.
804
+ * @param options.timeInForce - Time-in-force supported are: `GTC` (default), `FOK`, `IOC`. Param only for limit order
805
+ * @param options.stopLimitTimeInForce - Time-in-force supported are: `GTC` (default), `FOK`, `IOC`. Param only for limit order
806
+ * @returns An object with the result of the processed order or an error. See {@link IProcessOrder} for the returned data structure
807
+ */
808
+ OrderBook.prototype.createOrder = function (options) {
788
809
  switch (options.type) {
789
810
  case types_1.OrderType.MARKET:
790
811
  return this.market(options);
@@ -807,43 +828,49 @@ var OrderBook = /** @class */ (function () {
807
828
  };
808
829
  }
809
830
  };
810
- OrderBook.prototype.market = function (sideOrOptions, size) {
811
- // We don't want to test the deprecated signature.
812
- /* node:coverage disable */
813
- if (typeof sideOrOptions === "string" && size !== undefined) {
814
- return this._market({ side: sideOrOptions, size: size });
815
- /* node:coverage enable */
816
- }
817
- if (typeof sideOrOptions === "object") {
818
- return this._market(sideOrOptions);
819
- /* node:coverage disable */
831
+ /**
832
+ * Create a market order. See {@link MarketOrderOptions} for details.
833
+ *
834
+ * @param options
835
+ * @param options.side - `sell` or `buy`
836
+ * @param options.size - How much of currency you want to trade in units of base currency
837
+ * @returns An object with the result of the processed order or an error. See {@link IProcessOrder} for the returned data structure
838
+ */
839
+ OrderBook.prototype.market = function (options) {
840
+ var response = this._market(options);
841
+ if (this.enableJournaling && response.err === null) {
842
+ response.log = {
843
+ opId: ++this._lastOp,
844
+ ts: Date.now(),
845
+ op: "m",
846
+ o: options,
847
+ };
820
848
  }
821
- throw new Error("Invalid arguments.");
822
- /* node:coverage enable */
849
+ return response;
823
850
  };
824
- OrderBook.prototype.limit = function (sideOrOptions, orderID, size, price, timeInForce) {
825
- if (timeInForce === void 0) { timeInForce = types_1.TimeInForce.GTC; }
826
- // We don't want to test the deprecated signature.
827
- /* node:coverage disable */
828
- if (typeof sideOrOptions === "string" &&
829
- orderID !== undefined &&
830
- size !== undefined &&
831
- price !== undefined) {
832
- return this._limit({
833
- id: orderID,
834
- side: sideOrOptions,
835
- size: size,
836
- price: price,
837
- timeInForce: timeInForce,
838
- });
839
- /* node:coverage enable */
840
- }
841
- if (typeof sideOrOptions === "object") {
842
- return this._limit(sideOrOptions);
843
- /* node:coverage disable */
851
+ /**
852
+ * Create a limit order. See {@link LimitOrderOptions} for details.
853
+ *
854
+ * @param options
855
+ * @param options.side - `sell` or `buy`
856
+ * @param options.id - Unique order ID
857
+ * @param options.size - How much of currency you want to trade in units of base currency
858
+ * @param options.price - The price at which the order is to be fullfilled, in units of the quote currency
859
+ * @param options.postOnly - When `true` the order will be rejected if immediately matches and trades as a taker. Default is `false`
860
+ * @param options.timeInForce - Time-in-force type supported are: GTC, FOK, IOC. Default is GTC
861
+ * @returns An object with the result of the processed order or an error. See {@link IProcessOrder} for the returned data structure
862
+ */
863
+ OrderBook.prototype.limit = function (options) {
864
+ var response = this._limit(options);
865
+ if (this.enableJournaling && response.err === null) {
866
+ response.log = {
867
+ opId: ++this._lastOp,
868
+ ts: Date.now(),
869
+ op: "l",
870
+ o: options,
871
+ };
844
872
  }
845
- throw new Error("Invalid arguments.");
846
- /* node:coverage enable */
873
+ return response;
847
874
  };
848
875
  return OrderBook;
849
876
  }());
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.safeParse = exports.safeStringify = void 0;
4
+ /* node:coverage ignore next - Don't know why this line is uncovered */
5
+ // biome-ignore lint/suspicious/noExplicitAny: <explanation>
6
+ var safeStringify = function (value) {
7
+ try {
8
+ return JSON.stringify(value);
9
+ }
10
+ catch (error) {
11
+ return null;
12
+ }
13
+ };
14
+ exports.safeStringify = safeStringify;
15
+ /* node:coverage ignore next - Don't know why this line is uncovered */
16
+ var safeParse = function (value) {
17
+ try {
18
+ return JSON.parse(value);
19
+ }
20
+ catch (error) {
21
+ return null;
22
+ }
23
+ };
24
+ exports.safeParse = safeParse;
package/dist/esm/order.js CHANGED
@@ -17,6 +17,7 @@ var __extends = (this && this.__extends) || (function () {
17
17
  import { randomUUID } from "node:crypto";
18
18
  import { CustomError, ERROR } from "./errors";
19
19
  import { OrderType, } from "./types";
20
+ import { safeStringify } from "./utils";
20
21
  var BaseOrder = /** @class */ (function () {
21
22
  function BaseOrder(options) {
22
23
  var _a, _b;
@@ -75,7 +76,7 @@ var LimitOrder = /** @class */ (function (_super) {
75
76
  _this.toString = function () {
76
77
  return "".concat(_this._id, ":\n type: ").concat(_this.type, "\n side: ").concat(_this._side, "\n size: ").concat(_this._size, "\n origSize: ").concat(_this._origSize, "\n price: ").concat(_this._price, "\n time: ").concat(_this._time, "\n timeInForce: ").concat(_this._timeInForce, "\n makerQty: ").concat(_this._makerQty, "\n takerQty: ").concat(_this._takerQty);
77
78
  };
78
- _this.toJSON = function () { return JSON.stringify(_this.toObject()); };
79
+ _this.toJSON = function () { return safeStringify(_this.toObject()); };
79
80
  _this.toObject = function () { return ({
80
81
  id: _this._id,
81
82
  type: _this.type,
@@ -175,7 +176,7 @@ var StopMarketOrder = /** @class */ (function (_super) {
175
176
  _this.toString = function () {
176
177
  return "".concat(_this._id, ":\n type: ").concat(_this.type, "\n side: ").concat(_this._side, "\n size: ").concat(_this._size, "\n stopPrice: ").concat(_this._stopPrice, "\n time: ").concat(_this._time);
177
178
  };
178
- _this.toJSON = function () { return JSON.stringify(_this.toObject()); };
179
+ _this.toJSON = function () { return safeStringify(_this.toObject()); };
179
180
  _this.toObject = function () { return ({
180
181
  id: _this._id,
181
182
  type: _this.type,
@@ -213,9 +214,9 @@ var StopLimitOrder = /** @class */ (function (_super) {
213
214
  var _a;
214
215
  var _this = _super.call(this, options) || this;
215
216
  _this.toString = function () {
216
- return "".concat(_this._id, ":\n type: ").concat(_this.type, "\n side: ").concat(_this._side, "\n size: ").concat(_this._size, "\n price: ").concat(_this._price, "\n stopPrice: ").concat(_this._stopPrice, "\n timeInForce: ").concat(_this._timeInForce, "\n time: ").concat(_this._time);
217
+ return "".concat(_this._id, ":\n type: ").concat(_this.type, "\n side: ").concat(_this._side, "\n size: ").concat(_this._size, "\n price: ").concat(_this._price, "\n stopPrice: ").concat(_this._stopPrice, "\n\tisOCO: ").concat(_this.isOCO, "\n timeInForce: ").concat(_this._timeInForce, "\n time: ").concat(_this._time);
217
218
  };
218
- _this.toJSON = function () { return JSON.stringify(_this.toObject()); };
219
+ _this.toJSON = function () { return safeStringify(_this.toObject()); };
219
220
  _this.toObject = function () { return ({
220
221
  id: _this._id,
221
222
  type: _this.type,
@@ -223,6 +224,7 @@ var StopLimitOrder = /** @class */ (function (_super) {
223
224
  size: _this._size,
224
225
  price: _this._price,
225
226
  stopPrice: _this._stopPrice,
227
+ isOCO: _this.isOCO,
226
228
  timeInForce: _this._timeInForce,
227
229
  time: _this._time,
228
230
  }); };