nodejs-order-book 8.1.0 → 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 +11 -7
- package/dist/cjs/order.js +6 -4
- package/dist/cjs/orderbook.js +23 -17
- package/dist/cjs/utils.js +24 -0
- package/dist/esm/order.js +6 -4
- package/dist/esm/orderbook.js +23 -17
- package/dist/esm/utils.js +19 -0
- package/dist/types/order.d.ts +3 -3
- package/dist/types/orderbook.d.ts +2 -3
- package/dist/types/types.d.ts +10 -7
- package/dist/types/utils.d.ts +2 -0
- package/dist/umd/index.js +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -9,9 +9,10 @@
|
|
|
9
9
|
|
|
10
10
|
# Node.js Order Book
|
|
11
11
|
|
|
12
|
-
|
|
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>
|
|
15
16
|
|
|
16
17
|
## Table of Contents
|
|
17
18
|
|
|
@@ -42,15 +43,16 @@ Ultra-fast Node.js Order Book written in TypeScript for high-frequency trading (
|
|
|
42
43
|
- [License](#license)
|
|
43
44
|
|
|
44
45
|
## Features
|
|
46
|
+
> Initially ported from [Go orderbook](https://github.com/i25959341/orderbook), this order book has been enhanced with new features
|
|
45
47
|
|
|
46
48
|
- Standard price-time priority
|
|
47
49
|
- Supports both market and limit orders
|
|
48
|
-
- Supports `post-only` limit order
|
|
49
|
-
- Supports conditional orders [**Stop Limit, Stop Market and OCO**](#conditional-orders-) <img src="https://img.shields.io/badge/Experimental-blue" alt="Experimental">
|
|
50
|
-
- 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">
|
|
51
53
|
- Supports order cancelling
|
|
52
|
-
- Supports order price and/or size updating
|
|
53
|
-
- 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">
|
|
54
56
|
- **High performance (above 300k trades per second)**
|
|
55
57
|
|
|
56
58
|
**Machine:** ASUS ExpertBook, 11th Gen Intel(R) Core(TM) i7-1165G7, 2.80Ghz, 16GB RAM, Node.js v18.4.0.
|
|
@@ -391,6 +393,8 @@ A `snapshot` represents the state of the order book at a specific point in time.
|
|
|
391
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.
|
|
392
394
|
After taking the snapshot, you can safely remove all logs preceding the `lastOp` id.
|
|
393
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
|
+
|
|
394
398
|
```js
|
|
395
399
|
const ob = new OrderBook({ enableJournaling: true})
|
|
396
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
|
|
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
|
|
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
|
|
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
|
}); };
|
package/dist/cjs/orderbook.js
CHANGED
|
@@ -190,7 +190,8 @@ var OrderBook = /** @class */ (function () {
|
|
|
190
190
|
* @returns The order if exists or `undefined`
|
|
191
191
|
*/
|
|
192
192
|
this.order = function (orderID) {
|
|
193
|
-
|
|
193
|
+
var _a;
|
|
194
|
+
return (_a = _this.orders[orderID]) === null || _a === void 0 ? void 0 : _a.toObject();
|
|
194
195
|
};
|
|
195
196
|
// Returns price levels and volume at price level
|
|
196
197
|
this.depth = function () {
|
|
@@ -246,10 +247,10 @@ var OrderBook = /** @class */ (function () {
|
|
|
246
247
|
var bids = [];
|
|
247
248
|
var asks = [];
|
|
248
249
|
_this.bids.priceTree().forEach(function (price, orders) {
|
|
249
|
-
bids.push({ price: price, orders: orders.toArray() });
|
|
250
|
+
bids.push({ price: price, orders: orders.toArray().map(function (o) { return o.toObject(); }) });
|
|
250
251
|
});
|
|
251
252
|
_this.asks.priceTree().forEach(function (price, orders) {
|
|
252
|
-
asks.push({ price: price, orders: orders.toArray() });
|
|
253
|
+
asks.push({ price: price, orders: orders.toArray().map(function (o) { return o.toObject(); }) });
|
|
253
254
|
});
|
|
254
255
|
return { bids: bids, asks: asks, ts: Date.now(), lastOp: _this._lastOp };
|
|
255
256
|
};
|
|
@@ -336,7 +337,7 @@ var OrderBook = /** @class */ (function () {
|
|
|
336
337
|
isOCO: true,
|
|
337
338
|
});
|
|
338
339
|
_this.stopBook.add(stopLimit);
|
|
339
|
-
response.done.push(stopLimit);
|
|
340
|
+
response.done.push(stopLimit.toObject());
|
|
340
341
|
}
|
|
341
342
|
else {
|
|
342
343
|
response.err = (0, errors_1.CustomError)(errors_1.ERROR.INVALID_CONDITIONAL_ORDER);
|
|
@@ -346,7 +347,7 @@ var OrderBook = /** @class */ (function () {
|
|
|
346
347
|
this._stopOrder = function (stopOrder, response) {
|
|
347
348
|
if (_this.stopBook.validConditionalOrder(_this._marketPrice, stopOrder)) {
|
|
348
349
|
_this.stopBook.add(stopOrder);
|
|
349
|
-
response.done.push(stopOrder);
|
|
350
|
+
response.done.push(stopOrder.toObject());
|
|
350
351
|
}
|
|
351
352
|
else {
|
|
352
353
|
response.err = (0, errors_1.CustomError)(errors_1.ERROR.INVALID_CONDITIONAL_ORDER);
|
|
@@ -359,16 +360,18 @@ var OrderBook = /** @class */ (function () {
|
|
|
359
360
|
var level = _a[_i];
|
|
360
361
|
for (var _b = 0, _c = level.orders; _b < _c.length; _b++) {
|
|
361
362
|
var order = _c[_b];
|
|
362
|
-
|
|
363
|
-
_this.
|
|
363
|
+
var newOrder = order_1.OrderFactory.createOrder(order);
|
|
364
|
+
_this.orders[newOrder.id] = newOrder;
|
|
365
|
+
_this.bids.append(newOrder);
|
|
364
366
|
}
|
|
365
367
|
}
|
|
366
368
|
for (var _d = 0, _e = snapshot.asks; _d < _e.length; _d++) {
|
|
367
369
|
var level = _e[_d];
|
|
368
370
|
for (var _f = 0, _g = level.orders; _f < _g.length; _f++) {
|
|
369
371
|
var order = _g[_f];
|
|
370
|
-
|
|
371
|
-
_this.
|
|
372
|
+
var newOrder = order_1.OrderFactory.createOrder(order);
|
|
373
|
+
_this.orders[newOrder.id] = newOrder;
|
|
374
|
+
_this.asks.append(newOrder);
|
|
372
375
|
}
|
|
373
376
|
}
|
|
374
377
|
};
|
|
@@ -379,6 +382,7 @@ var OrderBook = /** @class */ (function () {
|
|
|
379
382
|
* @returns The removed order if exists or `undefined`
|
|
380
383
|
*/
|
|
381
384
|
this._cancelOrder = function (orderID, internalDeletion) {
|
|
385
|
+
var _a, _b;
|
|
382
386
|
if (internalDeletion === void 0) { internalDeletion = false; }
|
|
383
387
|
var order = _this.orders[orderID];
|
|
384
388
|
if (order === undefined)
|
|
@@ -386,11 +390,12 @@ var OrderBook = /** @class */ (function () {
|
|
|
386
390
|
delete _this.orders[orderID];
|
|
387
391
|
var side = order.side === types_1.Side.BUY ? _this.bids : _this.asks;
|
|
388
392
|
var response = {
|
|
389
|
-
order: side.remove(order),
|
|
393
|
+
order: (_a = side.remove(order)) === null || _a === void 0 ? void 0 : _a.toObject(),
|
|
390
394
|
};
|
|
391
395
|
// Delete OCO Order only when the delete request comes from user
|
|
392
396
|
if (!internalDeletion && order.ocoStopPrice !== undefined) {
|
|
393
|
-
response.stopOrder =
|
|
397
|
+
response.stopOrder = (_b = _this.stopBook
|
|
398
|
+
.remove(order.side, orderID, order.ocoStopPrice)) === null || _b === void 0 ? void 0 : _b.toObject();
|
|
394
399
|
}
|
|
395
400
|
if (_this.enableJournaling) {
|
|
396
401
|
response.log = {
|
|
@@ -463,7 +468,7 @@ var OrderBook = /** @class */ (function () {
|
|
|
463
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 } : {})));
|
|
464
469
|
if (response.done.length > 0) {
|
|
465
470
|
response.partialQuantityProcessed = size - quantityToTrade;
|
|
466
|
-
response.partial = order;
|
|
471
|
+
response.partial = order.toObject();
|
|
467
472
|
}
|
|
468
473
|
_this.orders[orderID] = sideToAdd.append(order);
|
|
469
474
|
}
|
|
@@ -492,7 +497,7 @@ var OrderBook = /** @class */ (function () {
|
|
|
492
497
|
takerQty: takerQty,
|
|
493
498
|
makerQty: makerQty,
|
|
494
499
|
});
|
|
495
|
-
response.done.push(order);
|
|
500
|
+
response.done.push(order.toObject());
|
|
496
501
|
}
|
|
497
502
|
// If IOC order was not matched completely remove from the order book
|
|
498
503
|
if (timeInForce === types_1.TimeInForce.IOC && response.quantityLeft > 0) {
|
|
@@ -538,7 +543,7 @@ var OrderBook = /** @class */ (function () {
|
|
|
538
543
|
timeInForce: stopOrder.timeInForce,
|
|
539
544
|
}, response);
|
|
540
545
|
}
|
|
541
|
-
response.activated.push(stopOrder);
|
|
546
|
+
response.activated.push(stopOrder.toObject());
|
|
542
547
|
});
|
|
543
548
|
}
|
|
544
549
|
};
|
|
@@ -650,10 +655,11 @@ var OrderBook = /** @class */ (function () {
|
|
|
650
655
|
var headOrder = orderQueue.head();
|
|
651
656
|
if (headOrder !== undefined) {
|
|
652
657
|
if (response.quantityLeft < headOrder.size) {
|
|
653
|
-
|
|
654
|
-
|
|
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;
|
|
655
661
|
response.partialQuantityProcessed = response.quantityLeft;
|
|
656
|
-
orderQueue.update(headOrder,
|
|
662
|
+
orderQueue.update(headOrder, partial);
|
|
657
663
|
response.quantityLeft = 0;
|
|
658
664
|
}
|
|
659
665
|
else {
|
|
@@ -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
|
|
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
|
|
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
|
|
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
|
}); };
|
package/dist/esm/orderbook.js
CHANGED
|
@@ -187,7 +187,8 @@ var OrderBook = /** @class */ (function () {
|
|
|
187
187
|
* @returns The order if exists or `undefined`
|
|
188
188
|
*/
|
|
189
189
|
this.order = function (orderID) {
|
|
190
|
-
|
|
190
|
+
var _a;
|
|
191
|
+
return (_a = _this.orders[orderID]) === null || _a === void 0 ? void 0 : _a.toObject();
|
|
191
192
|
};
|
|
192
193
|
// Returns price levels and volume at price level
|
|
193
194
|
this.depth = function () {
|
|
@@ -243,10 +244,10 @@ var OrderBook = /** @class */ (function () {
|
|
|
243
244
|
var bids = [];
|
|
244
245
|
var asks = [];
|
|
245
246
|
_this.bids.priceTree().forEach(function (price, orders) {
|
|
246
|
-
bids.push({ price: price, orders: orders.toArray() });
|
|
247
|
+
bids.push({ price: price, orders: orders.toArray().map(function (o) { return o.toObject(); }) });
|
|
247
248
|
});
|
|
248
249
|
_this.asks.priceTree().forEach(function (price, orders) {
|
|
249
|
-
asks.push({ price: price, orders: orders.toArray() });
|
|
250
|
+
asks.push({ price: price, orders: orders.toArray().map(function (o) { return o.toObject(); }) });
|
|
250
251
|
});
|
|
251
252
|
return { bids: bids, asks: asks, ts: Date.now(), lastOp: _this._lastOp };
|
|
252
253
|
};
|
|
@@ -333,7 +334,7 @@ var OrderBook = /** @class */ (function () {
|
|
|
333
334
|
isOCO: true,
|
|
334
335
|
});
|
|
335
336
|
_this.stopBook.add(stopLimit);
|
|
336
|
-
response.done.push(stopLimit);
|
|
337
|
+
response.done.push(stopLimit.toObject());
|
|
337
338
|
}
|
|
338
339
|
else {
|
|
339
340
|
response.err = CustomError(ERROR.INVALID_CONDITIONAL_ORDER);
|
|
@@ -343,7 +344,7 @@ var OrderBook = /** @class */ (function () {
|
|
|
343
344
|
this._stopOrder = function (stopOrder, response) {
|
|
344
345
|
if (_this.stopBook.validConditionalOrder(_this._marketPrice, stopOrder)) {
|
|
345
346
|
_this.stopBook.add(stopOrder);
|
|
346
|
-
response.done.push(stopOrder);
|
|
347
|
+
response.done.push(stopOrder.toObject());
|
|
347
348
|
}
|
|
348
349
|
else {
|
|
349
350
|
response.err = CustomError(ERROR.INVALID_CONDITIONAL_ORDER);
|
|
@@ -356,16 +357,18 @@ var OrderBook = /** @class */ (function () {
|
|
|
356
357
|
var level = _a[_i];
|
|
357
358
|
for (var _b = 0, _c = level.orders; _b < _c.length; _b++) {
|
|
358
359
|
var order = _c[_b];
|
|
359
|
-
|
|
360
|
-
_this.
|
|
360
|
+
var newOrder = OrderFactory.createOrder(order);
|
|
361
|
+
_this.orders[newOrder.id] = newOrder;
|
|
362
|
+
_this.bids.append(newOrder);
|
|
361
363
|
}
|
|
362
364
|
}
|
|
363
365
|
for (var _d = 0, _e = snapshot.asks; _d < _e.length; _d++) {
|
|
364
366
|
var level = _e[_d];
|
|
365
367
|
for (var _f = 0, _g = level.orders; _f < _g.length; _f++) {
|
|
366
368
|
var order = _g[_f];
|
|
367
|
-
|
|
368
|
-
_this.
|
|
369
|
+
var newOrder = OrderFactory.createOrder(order);
|
|
370
|
+
_this.orders[newOrder.id] = newOrder;
|
|
371
|
+
_this.asks.append(newOrder);
|
|
369
372
|
}
|
|
370
373
|
}
|
|
371
374
|
};
|
|
@@ -376,6 +379,7 @@ var OrderBook = /** @class */ (function () {
|
|
|
376
379
|
* @returns The removed order if exists or `undefined`
|
|
377
380
|
*/
|
|
378
381
|
this._cancelOrder = function (orderID, internalDeletion) {
|
|
382
|
+
var _a, _b;
|
|
379
383
|
if (internalDeletion === void 0) { internalDeletion = false; }
|
|
380
384
|
var order = _this.orders[orderID];
|
|
381
385
|
if (order === undefined)
|
|
@@ -383,11 +387,12 @@ var OrderBook = /** @class */ (function () {
|
|
|
383
387
|
delete _this.orders[orderID];
|
|
384
388
|
var side = order.side === Side.BUY ? _this.bids : _this.asks;
|
|
385
389
|
var response = {
|
|
386
|
-
order: side.remove(order),
|
|
390
|
+
order: (_a = side.remove(order)) === null || _a === void 0 ? void 0 : _a.toObject(),
|
|
387
391
|
};
|
|
388
392
|
// Delete OCO Order only when the delete request comes from user
|
|
389
393
|
if (!internalDeletion && order.ocoStopPrice !== undefined) {
|
|
390
|
-
response.stopOrder =
|
|
394
|
+
response.stopOrder = (_b = _this.stopBook
|
|
395
|
+
.remove(order.side, orderID, order.ocoStopPrice)) === null || _b === void 0 ? void 0 : _b.toObject();
|
|
391
396
|
}
|
|
392
397
|
if (_this.enableJournaling) {
|
|
393
398
|
response.log = {
|
|
@@ -460,7 +465,7 @@ var OrderBook = /** @class */ (function () {
|
|
|
460
465
|
order = OrderFactory.createOrder(__assign({ type: 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 } : {})));
|
|
461
466
|
if (response.done.length > 0) {
|
|
462
467
|
response.partialQuantityProcessed = size - quantityToTrade;
|
|
463
|
-
response.partial = order;
|
|
468
|
+
response.partial = order.toObject();
|
|
464
469
|
}
|
|
465
470
|
_this.orders[orderID] = sideToAdd.append(order);
|
|
466
471
|
}
|
|
@@ -489,7 +494,7 @@ var OrderBook = /** @class */ (function () {
|
|
|
489
494
|
takerQty: takerQty,
|
|
490
495
|
makerQty: makerQty,
|
|
491
496
|
});
|
|
492
|
-
response.done.push(order);
|
|
497
|
+
response.done.push(order.toObject());
|
|
493
498
|
}
|
|
494
499
|
// If IOC order was not matched completely remove from the order book
|
|
495
500
|
if (timeInForce === TimeInForce.IOC && response.quantityLeft > 0) {
|
|
@@ -535,7 +540,7 @@ var OrderBook = /** @class */ (function () {
|
|
|
535
540
|
timeInForce: stopOrder.timeInForce,
|
|
536
541
|
}, response);
|
|
537
542
|
}
|
|
538
|
-
response.activated.push(stopOrder);
|
|
543
|
+
response.activated.push(stopOrder.toObject());
|
|
539
544
|
});
|
|
540
545
|
}
|
|
541
546
|
};
|
|
@@ -647,10 +652,11 @@ var OrderBook = /** @class */ (function () {
|
|
|
647
652
|
var headOrder = orderQueue.head();
|
|
648
653
|
if (headOrder !== undefined) {
|
|
649
654
|
if (response.quantityLeft < headOrder.size) {
|
|
650
|
-
|
|
651
|
-
|
|
655
|
+
var partial = OrderFactory.createOrder(__assign(__assign({}, headOrder.toObject()), { size: headOrder.size - response.quantityLeft }));
|
|
656
|
+
response.partial = partial.toObject();
|
|
657
|
+
_this.orders[headOrder.id] = partial;
|
|
652
658
|
response.partialQuantityProcessed = response.quantityLeft;
|
|
653
|
-
orderQueue.update(headOrder,
|
|
659
|
+
orderQueue.update(headOrder, partial);
|
|
654
660
|
response.quantityLeft = 0;
|
|
655
661
|
}
|
|
656
662
|
else {
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/* node:coverage ignore next - Don't know why this line is uncovered */
|
|
2
|
+
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
|
|
3
|
+
export var safeStringify = function (value) {
|
|
4
|
+
try {
|
|
5
|
+
return JSON.stringify(value);
|
|
6
|
+
}
|
|
7
|
+
catch (error) {
|
|
8
|
+
return null;
|
|
9
|
+
}
|
|
10
|
+
};
|
|
11
|
+
/* node:coverage ignore next - Don't know why this line is uncovered */
|
|
12
|
+
export var safeParse = function (value) {
|
|
13
|
+
try {
|
|
14
|
+
return JSON.parse(value);
|
|
15
|
+
}
|
|
16
|
+
catch (error) {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
};
|
package/dist/types/order.d.ts
CHANGED
|
@@ -35,7 +35,7 @@ export declare class LimitOrder extends BaseOrder {
|
|
|
35
35
|
get takerQty(): number;
|
|
36
36
|
get ocoStopPrice(): number | undefined;
|
|
37
37
|
toString: () => string;
|
|
38
|
-
toJSON: () => string;
|
|
38
|
+
toJSON: () => string | null;
|
|
39
39
|
toObject: () => ILimitOrder;
|
|
40
40
|
}
|
|
41
41
|
export declare class StopMarketOrder extends BaseOrder {
|
|
@@ -45,7 +45,7 @@ export declare class StopMarketOrder extends BaseOrder {
|
|
|
45
45
|
get type(): OrderType.STOP_MARKET;
|
|
46
46
|
get stopPrice(): number;
|
|
47
47
|
toString: () => string;
|
|
48
|
-
toJSON: () => string;
|
|
48
|
+
toJSON: () => string | null;
|
|
49
49
|
toObject: () => IStopMarketOrder;
|
|
50
50
|
}
|
|
51
51
|
export declare class StopLimitOrder extends BaseOrder {
|
|
@@ -62,7 +62,7 @@ export declare class StopLimitOrder extends BaseOrder {
|
|
|
62
62
|
get timeInForce(): TimeInForce;
|
|
63
63
|
get isOCO(): boolean;
|
|
64
64
|
toString: () => string;
|
|
65
|
-
toJSON: () => string;
|
|
65
|
+
toJSON: () => string | null;
|
|
66
66
|
toObject: () => IStopLimitOrder;
|
|
67
67
|
}
|
|
68
68
|
export declare const OrderFactory: {
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { type OrderBookError } from "./errors";
|
|
2
|
-
import { type
|
|
3
|
-
import { type CreateOrderOptions, type ICancelOrder, type IProcessOrder, type LimitOrderOptions, type MarketOrderOptions, type OCOOrderOptions, type OrderBookOptions, type OrderUpdatePrice, type OrderUpdateSize, Side, type Snapshot, type StopLimitOrderOptions, type StopMarketOrderOptions } from "./types";
|
|
2
|
+
import { type CreateOrderOptions, type ICancelOrder, type ILimitOrder, type IProcessOrder, type LimitOrderOptions, type MarketOrderOptions, type OCOOrderOptions, type OrderBookOptions, type OrderUpdatePrice, type OrderUpdateSize, Side, type Snapshot, type StopLimitOrderOptions, type StopMarketOrderOptions } from "./types";
|
|
4
3
|
export declare class OrderBook {
|
|
5
4
|
private orders;
|
|
6
5
|
private _lastOp;
|
|
@@ -131,7 +130,7 @@ export declare class OrderBook {
|
|
|
131
130
|
* @param orderID - The ID of the order to be returned
|
|
132
131
|
* @returns The order if exists or `undefined`
|
|
133
132
|
*/
|
|
134
|
-
order: (orderID: string) =>
|
|
133
|
+
order: (orderID: string) => ILimitOrder | undefined;
|
|
135
134
|
depth: () => [Array<[number, number]>, Array<[number, number]>];
|
|
136
135
|
toString: () => string;
|
|
137
136
|
calculateMarketPrice: (side: Side, size: number) => {
|
package/dist/types/types.d.ts
CHANGED
|
@@ -140,22 +140,25 @@ export interface IStopLimitOrder {
|
|
|
140
140
|
stopPrice: number;
|
|
141
141
|
timeInForce: TimeInForce;
|
|
142
142
|
time: number;
|
|
143
|
+
isOCO: boolean;
|
|
143
144
|
}
|
|
144
145
|
/**
|
|
145
146
|
* Represents an order in the order book.
|
|
146
147
|
*/
|
|
147
148
|
export type OrderOptions = InternalMarketOrderOptions | InternalLimitOrderOptions | InternalStopLimitOrderOptions | InternalStopMarketOrderOptions;
|
|
148
149
|
export type StopOrderOptions = StopMarketOrderOptions | StopLimitOrderOptions | OCOOrderOptions;
|
|
150
|
+
export type IStopOrder = IStopLimitOrder | IStopMarketOrder;
|
|
151
|
+
export type IOrder = ILimitOrder | IStopOrder;
|
|
149
152
|
/**
|
|
150
153
|
* Represents the result of processing an order.
|
|
151
154
|
*/
|
|
152
155
|
export interface IProcessOrder {
|
|
153
156
|
/** Array of fully processed orders. */
|
|
154
|
-
done:
|
|
157
|
+
done: IOrder[];
|
|
155
158
|
/** Array of activated (stop limit or stop market) orders */
|
|
156
|
-
activated:
|
|
159
|
+
activated: IStopOrder[];
|
|
157
160
|
/** The partially processed order, if any. */
|
|
158
|
-
partial:
|
|
161
|
+
partial: ILimitOrder | null;
|
|
159
162
|
/** The quantity that has been processed in the partial order. */
|
|
160
163
|
partialQuantityProcessed: number;
|
|
161
164
|
/** The remaining quantity that needs to be processed. */
|
|
@@ -294,8 +297,8 @@ export type CreateOrderOptions = ({
|
|
|
294
297
|
* Represents a cancel order operation.
|
|
295
298
|
*/
|
|
296
299
|
export interface ICancelOrder {
|
|
297
|
-
order:
|
|
298
|
-
stopOrder?:
|
|
300
|
+
order: ILimitOrder;
|
|
301
|
+
stopOrder?: IStopOrder;
|
|
299
302
|
/** Optional log related to the order cancellation. */
|
|
300
303
|
log?: CancelOrderJournalLog;
|
|
301
304
|
}
|
|
@@ -345,14 +348,14 @@ export interface Snapshot {
|
|
|
345
348
|
/** Price of the ask order */
|
|
346
349
|
price: number;
|
|
347
350
|
/** List of orders associated with this price */
|
|
348
|
-
orders:
|
|
351
|
+
orders: ILimitOrder[];
|
|
349
352
|
}>;
|
|
350
353
|
/** List of bid orders, each with a price and a list of associated orders */
|
|
351
354
|
bids: Array<{
|
|
352
355
|
/** Price of the bid order */
|
|
353
356
|
price: number;
|
|
354
357
|
/** List of orders associated with this price */
|
|
355
|
-
orders:
|
|
358
|
+
orders: ILimitOrder[];
|
|
356
359
|
}>;
|
|
357
360
|
/** Unix timestamp representing when the snapshot was taken */
|
|
358
361
|
ts: number;
|