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 +40 -8
- package/dist/cjs/order.js +6 -4
- package/dist/cjs/orderbook.js +153 -126
- package/dist/cjs/utils.js +24 -0
- package/dist/esm/order.js +6 -4
- package/dist/esm/orderbook.js +153 -126
- package/dist/esm/utils.js +19 -0
- package/dist/types/order.d.ts +3 -3
- package/dist/types/orderbook.d.ts +2 -47
- package/dist/types/types.d.ts +61 -20
- package/dist/types/utils.d.ts +2 -0
- package/dist/umd/index.js +1 -1
- package/package.json +10 -6
package/README.md
CHANGED
|
@@ -9,20 +9,50 @@
|
|
|
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>
|
|
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
|
|
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
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
289
|
+
if (response.err !== null)
|
|
270
290
|
return response;
|
|
271
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
358
|
-
_this.
|
|
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
|
-
|
|
366
|
-
_this.
|
|
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 =
|
|
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(
|
|
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
|
|
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
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
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
|
-
|
|
622
|
-
|
|
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,
|
|
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
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
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
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
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
|
-
|
|
822
|
-
/* node:coverage enable */
|
|
849
|
+
return response;
|
|
823
850
|
};
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
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
|
-
|
|
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
|
|
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
|
}); };
|