nodejs-order-book 10.0.1 → 10.2.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 +505 -157
- package/dist/cjs/errors.js +47 -45
- package/dist/cjs/index.js +4 -3
- package/dist/cjs/order.js +229 -272
- package/dist/cjs/orderbook.js +904 -784
- package/dist/cjs/orderqueue.js +68 -66
- package/dist/cjs/orderside.js +139 -147
- package/dist/cjs/stopbook.js +69 -69
- package/dist/cjs/stopqueue.js +47 -51
- package/dist/cjs/stopside.js +68 -68
- package/dist/cjs/types.js +8 -1
- package/dist/cjs/utils.js +2 -2
- package/dist/esm/errors.js +47 -46
- package/dist/esm/index.js +2 -2
- package/dist/esm/order.js +227 -273
- package/dist/esm/orderbook.js +900 -781
- package/dist/esm/orderqueue.js +67 -66
- package/dist/esm/orderside.js +134 -143
- package/dist/esm/stopbook.js +67 -68
- package/dist/esm/stopqueue.js +46 -51
- package/dist/esm/stopside.js +64 -65
- package/dist/esm/types.js +7 -0
- package/dist/esm/utils.js +2 -2
- package/dist/types/errors.d.ts +2 -1
- package/dist/types/index.d.ts +2 -2
- package/dist/types/order.d.ts +5 -1
- package/dist/types/orderbook.d.ts +28 -2
- package/dist/types/types.d.ts +24 -0
- package/package.json +13 -12
package/dist/esm/orderqueue.js
CHANGED
|
@@ -1,72 +1,73 @@
|
|
|
1
1
|
/* node:coverage ignore next - Don't know why first and last line of each file count as uncovered */
|
|
2
2
|
import Denque from "denque";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
};
|
|
11
|
-
this.toArray = function () {
|
|
12
|
-
return _this._orders.toArray();
|
|
13
|
-
};
|
|
14
|
-
// returns price level of the queue
|
|
15
|
-
this.price = function () {
|
|
16
|
-
return _this._price;
|
|
17
|
-
};
|
|
18
|
-
// returns price level of the queue
|
|
19
|
-
this.volume = function () {
|
|
20
|
-
return _this._volume;
|
|
21
|
-
};
|
|
22
|
-
// returns top order in queue
|
|
23
|
-
this.head = function () {
|
|
24
|
-
return _this._orders.peekFront();
|
|
25
|
-
};
|
|
26
|
-
// returns bottom order in queue
|
|
27
|
-
this.tail = function () {
|
|
28
|
-
return _this._orders.peekBack();
|
|
29
|
-
};
|
|
30
|
-
// adds order to tail of the queue
|
|
31
|
-
this.append = function (order) {
|
|
32
|
-
_this._volume += order.size;
|
|
33
|
-
_this._orders.push(order);
|
|
34
|
-
_this._ordersMap[order.id] = _this._orders.length - 1;
|
|
35
|
-
return order;
|
|
36
|
-
};
|
|
37
|
-
// sets up new order to list value
|
|
38
|
-
this.update = function (oldOrder, newOrder) {
|
|
39
|
-
_this._volume -= oldOrder.size;
|
|
40
|
-
_this._volume += newOrder.size;
|
|
41
|
-
// Remove old order from head
|
|
42
|
-
_this._orders.shift();
|
|
43
|
-
delete _this._ordersMap[oldOrder.id];
|
|
44
|
-
// Add new order to head
|
|
45
|
-
_this._orders.unshift(newOrder);
|
|
46
|
-
_this._ordersMap[newOrder.id] = 0;
|
|
47
|
-
};
|
|
48
|
-
// removes order from the queue
|
|
49
|
-
this.remove = function (order) {
|
|
50
|
-
_this._volume -= order.size;
|
|
51
|
-
var deletedOrderIndex = _this._ordersMap[order.id];
|
|
52
|
-
_this._orders.removeOne(deletedOrderIndex);
|
|
53
|
-
delete _this._ordersMap[order.id];
|
|
54
|
-
// Update all orders indexes where index is greater than the deleted one
|
|
55
|
-
for (var orderId in _this._ordersMap) {
|
|
56
|
-
if (_this._ordersMap[orderId] > deletedOrderIndex) {
|
|
57
|
-
_this._ordersMap[orderId] -= 1;
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
};
|
|
61
|
-
this.updateOrderSize = function (order, size) {
|
|
62
|
-
_this._volume += size - order.size; // update volume
|
|
63
|
-
order.size = size;
|
|
64
|
-
order.time = Date.now();
|
|
65
|
-
};
|
|
3
|
+
export class OrderQueue {
|
|
4
|
+
_price;
|
|
5
|
+
_volume;
|
|
6
|
+
// { orderID: index } index in denque
|
|
7
|
+
_orders;
|
|
8
|
+
_ordersMap = {};
|
|
9
|
+
constructor(price) {
|
|
66
10
|
this._price = price;
|
|
67
11
|
this._volume = 0;
|
|
68
12
|
this._orders = new Denque();
|
|
69
13
|
}
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
14
|
+
// returns the number of orders in queue
|
|
15
|
+
len = () => {
|
|
16
|
+
return this._orders.length;
|
|
17
|
+
};
|
|
18
|
+
toArray = () => {
|
|
19
|
+
return this._orders.toArray();
|
|
20
|
+
};
|
|
21
|
+
// returns price level of the queue
|
|
22
|
+
price = () => {
|
|
23
|
+
return this._price;
|
|
24
|
+
};
|
|
25
|
+
// returns price level of the queue
|
|
26
|
+
volume = () => {
|
|
27
|
+
return this._volume;
|
|
28
|
+
};
|
|
29
|
+
// returns top order in queue
|
|
30
|
+
head = () => {
|
|
31
|
+
return this._orders.peekFront();
|
|
32
|
+
};
|
|
33
|
+
// returns bottom order in queue
|
|
34
|
+
tail = () => {
|
|
35
|
+
return this._orders.peekBack();
|
|
36
|
+
};
|
|
37
|
+
// adds order to tail of the queue
|
|
38
|
+
append = (order) => {
|
|
39
|
+
this._volume += order.size;
|
|
40
|
+
this._orders.push(order);
|
|
41
|
+
this._ordersMap[order.id] = this._orders.length - 1;
|
|
42
|
+
return order;
|
|
43
|
+
};
|
|
44
|
+
// sets up new order to list value
|
|
45
|
+
update = (oldOrder, newOrder) => {
|
|
46
|
+
this._volume -= oldOrder.size;
|
|
47
|
+
this._volume += newOrder.size;
|
|
48
|
+
// Remove old order from head
|
|
49
|
+
this._orders.shift();
|
|
50
|
+
delete this._ordersMap[oldOrder.id];
|
|
51
|
+
// Add new order to head
|
|
52
|
+
this._orders.unshift(newOrder);
|
|
53
|
+
this._ordersMap[newOrder.id] = 0;
|
|
54
|
+
};
|
|
55
|
+
// removes order from the queue
|
|
56
|
+
remove = (order) => {
|
|
57
|
+
this._volume -= order.size;
|
|
58
|
+
const deletedOrderIndex = this._ordersMap[order.id];
|
|
59
|
+
this._orders.removeOne(deletedOrderIndex);
|
|
60
|
+
delete this._ordersMap[order.id];
|
|
61
|
+
// Update all orders indexes where index is greater than the deleted one
|
|
62
|
+
for (const orderId in this._ordersMap) {
|
|
63
|
+
if (this._ordersMap[orderId] > deletedOrderIndex) {
|
|
64
|
+
this._ordersMap[orderId] -= 1;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
updateOrderSize = (order, size) => {
|
|
69
|
+
this._volume += size - order.size; // update volume
|
|
70
|
+
order.size = size;
|
|
71
|
+
order.time = Date.now();
|
|
72
|
+
};
|
|
73
|
+
}
|
package/dist/esm/orderside.js
CHANGED
|
@@ -1,152 +1,143 @@
|
|
|
1
|
-
var __assign = (this && this.__assign) || function () {
|
|
2
|
-
__assign = Object.assign || function(t) {
|
|
3
|
-
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
4
|
-
s = arguments[i];
|
|
5
|
-
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
|
6
|
-
t[p] = s[p];
|
|
7
|
-
}
|
|
8
|
-
return t;
|
|
9
|
-
};
|
|
10
|
-
return __assign.apply(this, arguments);
|
|
11
|
-
};
|
|
12
1
|
/* node:coverage ignore next - Don't know why first and last line of each file count as uncovered */
|
|
13
2
|
import createRBTree from "functional-red-black-tree";
|
|
14
3
|
import { CustomError, ERROR } from "./errors";
|
|
15
4
|
import { OrderFactory } from "./order";
|
|
16
5
|
import { OrderQueue } from "./orderqueue";
|
|
17
6
|
import { Side } from "./types";
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
};
|
|
31
|
-
// returns depth of market
|
|
32
|
-
this.depth = function () {
|
|
33
|
-
return _this._depthSide;
|
|
34
|
-
};
|
|
35
|
-
// returns total amount of quantity in side
|
|
36
|
-
this.volume = function () {
|
|
37
|
-
return _this._volume;
|
|
38
|
-
};
|
|
39
|
-
// returns the total (size * price of each price level) in side
|
|
40
|
-
this.total = function () {
|
|
41
|
-
return _this._total;
|
|
42
|
-
};
|
|
43
|
-
// returns the price tree in side
|
|
44
|
-
this.priceTree = function () {
|
|
45
|
-
return _this._priceTree;
|
|
46
|
-
};
|
|
47
|
-
// appends order to definite price level
|
|
48
|
-
this.append = function (order) {
|
|
49
|
-
var price = order.price;
|
|
50
|
-
var strPrice = price.toString();
|
|
51
|
-
if (_this._prices[strPrice] === undefined) {
|
|
52
|
-
var priceQueue = new OrderQueue(price);
|
|
53
|
-
_this._prices[strPrice] = priceQueue;
|
|
54
|
-
_this._priceTree = _this._priceTree.insert(price, priceQueue);
|
|
55
|
-
_this._depthSide += 1;
|
|
56
|
-
}
|
|
57
|
-
_this._numOrders += 1;
|
|
58
|
-
_this._volume += order.size;
|
|
59
|
-
_this._total += order.size * order.price;
|
|
60
|
-
return _this._prices[strPrice].append(order);
|
|
61
|
-
};
|
|
62
|
-
// removes order from definite price level
|
|
63
|
-
this.remove = function (order) {
|
|
64
|
-
var price = order.price;
|
|
65
|
-
var strPrice = price.toString();
|
|
66
|
-
if (_this._prices[strPrice] === undefined) {
|
|
67
|
-
throw CustomError(ERROR.INVALID_PRICE_LEVEL);
|
|
68
|
-
}
|
|
69
|
-
_this._prices[strPrice].remove(order);
|
|
70
|
-
if (_this._prices[strPrice].len() === 0) {
|
|
71
|
-
delete _this._prices[strPrice];
|
|
72
|
-
_this._priceTree = _this._priceTree.remove(price);
|
|
73
|
-
_this._depthSide -= 1;
|
|
74
|
-
}
|
|
75
|
-
_this._numOrders -= 1;
|
|
76
|
-
_this._volume -= order.size;
|
|
77
|
-
_this._total -= order.size * order.price;
|
|
78
|
-
return order;
|
|
79
|
-
};
|
|
80
|
-
// Update the price of an order and return the order with the updated price
|
|
81
|
-
this.updateOrderPrice = function (oldOrder, orderUpdate) {
|
|
82
|
-
_this.remove(oldOrder);
|
|
83
|
-
var newOrder = OrderFactory.createOrder(__assign(__assign({}, oldOrder.toObject()), { size: orderUpdate.size !== undefined ? orderUpdate.size : oldOrder.size, price: orderUpdate.price, time: Date.now() }));
|
|
84
|
-
_this.append(newOrder);
|
|
85
|
-
return newOrder;
|
|
86
|
-
};
|
|
87
|
-
// Update the price of an order and return the order with the updated price
|
|
88
|
-
this.updateOrderSize = function (oldOrder, orderUpdate) {
|
|
89
|
-
var _a;
|
|
90
|
-
var newOrderPrice = (_a = orderUpdate.price) !== null && _a !== void 0 ? _a : oldOrder.price;
|
|
91
|
-
_this._volume += orderUpdate.size - oldOrder.size;
|
|
92
|
-
_this._total +=
|
|
93
|
-
orderUpdate.size * newOrderPrice - oldOrder.size * oldOrder.price;
|
|
94
|
-
_this._prices[oldOrder.price.toString()].updateOrderSize(oldOrder, orderUpdate.size);
|
|
95
|
-
return oldOrder;
|
|
96
|
-
};
|
|
97
|
-
// returns max level of price
|
|
98
|
-
this.maxPriceQueue = function () {
|
|
99
|
-
if (_this._depthSide > 0) {
|
|
100
|
-
var max = _this._side === Side.SELL ? _this._priceTree.end : _this._priceTree.begin;
|
|
101
|
-
return max.value;
|
|
102
|
-
}
|
|
103
|
-
};
|
|
104
|
-
// returns min level of price
|
|
105
|
-
this.minPriceQueue = function () {
|
|
106
|
-
if (_this._depthSide > 0) {
|
|
107
|
-
var min = _this._side === Side.SELL ? _this._priceTree.begin : _this._priceTree.end;
|
|
108
|
-
return min.value;
|
|
109
|
-
}
|
|
110
|
-
};
|
|
111
|
-
// returns nearest OrderQueue with price less than given
|
|
112
|
-
this.lowerThan = function (price) {
|
|
113
|
-
var node = _this._side === Side.SELL
|
|
114
|
-
? _this._priceTree.lt(price)
|
|
115
|
-
: _this._priceTree.gt(price);
|
|
116
|
-
return node.value;
|
|
117
|
-
};
|
|
118
|
-
// returns nearest OrderQueue with price greater than given
|
|
119
|
-
this.greaterThan = function (price) {
|
|
120
|
-
var node = _this._side === Side.SELL
|
|
121
|
-
? _this._priceTree.gt(price)
|
|
122
|
-
: _this._priceTree.lt(price);
|
|
123
|
-
return node.value;
|
|
124
|
-
};
|
|
125
|
-
// returns all orders
|
|
126
|
-
this.orders = function () {
|
|
127
|
-
var orders = [];
|
|
128
|
-
for (var price in _this._prices) {
|
|
129
|
-
var allOrders = _this._prices[price].toArray();
|
|
130
|
-
orders = orders.concat(allOrders);
|
|
131
|
-
}
|
|
132
|
-
return orders;
|
|
133
|
-
};
|
|
134
|
-
this.toString = function () {
|
|
135
|
-
var s = "";
|
|
136
|
-
var level = _this.maxPriceQueue();
|
|
137
|
-
while (level !== undefined) {
|
|
138
|
-
var volume = level.volume().toString();
|
|
139
|
-
s += "\n".concat(level.price(), " -> ").concat(volume);
|
|
140
|
-
level = _this.lowerThan(level.price());
|
|
141
|
-
}
|
|
142
|
-
return s;
|
|
143
|
-
};
|
|
144
|
-
var compare = side === Side.SELL
|
|
145
|
-
? function (a, b) { return a - b; }
|
|
146
|
-
: function (a, b) { return b - a; };
|
|
7
|
+
export class OrderSide {
|
|
8
|
+
_priceTree;
|
|
9
|
+
_prices = {};
|
|
10
|
+
_volume = 0;
|
|
11
|
+
_total = 0;
|
|
12
|
+
_numOrders = 0;
|
|
13
|
+
_depthSide = 0;
|
|
14
|
+
_side = Side.SELL;
|
|
15
|
+
constructor(side) {
|
|
16
|
+
const compare = side === Side.SELL
|
|
17
|
+
? (a, b) => a - b
|
|
18
|
+
: (a, b) => b - a;
|
|
147
19
|
this._priceTree = createRBTree(compare);
|
|
148
20
|
this._side = side;
|
|
149
21
|
}
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
22
|
+
// returns amount of orders
|
|
23
|
+
len = () => {
|
|
24
|
+
return this._numOrders;
|
|
25
|
+
};
|
|
26
|
+
// returns depth of market
|
|
27
|
+
depth = () => {
|
|
28
|
+
return this._depthSide;
|
|
29
|
+
};
|
|
30
|
+
// returns total amount of quantity in side
|
|
31
|
+
volume = () => {
|
|
32
|
+
return this._volume;
|
|
33
|
+
};
|
|
34
|
+
// returns the total (size * price of each price level) in side
|
|
35
|
+
total = () => {
|
|
36
|
+
return this._total;
|
|
37
|
+
};
|
|
38
|
+
// returns the price tree in side
|
|
39
|
+
priceTree = () => {
|
|
40
|
+
return this._priceTree;
|
|
41
|
+
};
|
|
42
|
+
// appends order to definite price level
|
|
43
|
+
append = (order) => {
|
|
44
|
+
const price = order.price;
|
|
45
|
+
const strPrice = price.toString();
|
|
46
|
+
if (this._prices[strPrice] === undefined) {
|
|
47
|
+
const priceQueue = new OrderQueue(price);
|
|
48
|
+
this._prices[strPrice] = priceQueue;
|
|
49
|
+
this._priceTree = this._priceTree.insert(price, priceQueue);
|
|
50
|
+
this._depthSide += 1;
|
|
51
|
+
}
|
|
52
|
+
this._numOrders += 1;
|
|
53
|
+
this._volume += order.size;
|
|
54
|
+
this._total += order.size * order.price;
|
|
55
|
+
return this._prices[strPrice].append(order);
|
|
56
|
+
};
|
|
57
|
+
// removes order from definite price level
|
|
58
|
+
remove = (order) => {
|
|
59
|
+
const price = order.price;
|
|
60
|
+
const strPrice = price.toString();
|
|
61
|
+
if (this._prices[strPrice] === undefined) {
|
|
62
|
+
throw CustomError(ERROR.INVALID_PRICE_LEVEL);
|
|
63
|
+
}
|
|
64
|
+
this._prices[strPrice].remove(order);
|
|
65
|
+
if (this._prices[strPrice].len() === 0) {
|
|
66
|
+
delete this._prices[strPrice];
|
|
67
|
+
this._priceTree = this._priceTree.remove(price);
|
|
68
|
+
this._depthSide -= 1;
|
|
69
|
+
}
|
|
70
|
+
this._numOrders -= 1;
|
|
71
|
+
this._volume -= order.size;
|
|
72
|
+
this._total -= order.size * order.price;
|
|
73
|
+
return order;
|
|
74
|
+
};
|
|
75
|
+
// Update the price of an order and return the order with the updated price
|
|
76
|
+
updateOrderPrice = (oldOrder, orderUpdate) => {
|
|
77
|
+
this.remove(oldOrder);
|
|
78
|
+
const newOrder = OrderFactory.createOrder({
|
|
79
|
+
...oldOrder.toObject(),
|
|
80
|
+
size: orderUpdate.size !== undefined ? orderUpdate.size : oldOrder.size,
|
|
81
|
+
price: orderUpdate.price,
|
|
82
|
+
time: Date.now(),
|
|
83
|
+
});
|
|
84
|
+
this.append(newOrder);
|
|
85
|
+
return newOrder;
|
|
86
|
+
};
|
|
87
|
+
// Update the price of an order and return the order with the updated price
|
|
88
|
+
updateOrderSize = (oldOrder, orderUpdate) => {
|
|
89
|
+
const newOrderPrice = orderUpdate.price ?? oldOrder.price;
|
|
90
|
+
this._volume += orderUpdate.size - oldOrder.size;
|
|
91
|
+
this._total +=
|
|
92
|
+
orderUpdate.size * newOrderPrice - oldOrder.size * oldOrder.price;
|
|
93
|
+
this._prices[oldOrder.price.toString()].updateOrderSize(oldOrder, orderUpdate.size);
|
|
94
|
+
return oldOrder;
|
|
95
|
+
};
|
|
96
|
+
// returns max level of price
|
|
97
|
+
maxPriceQueue = () => {
|
|
98
|
+
if (this._depthSide > 0) {
|
|
99
|
+
const max = this._side === Side.SELL ? this._priceTree.end : this._priceTree.begin;
|
|
100
|
+
return max.value;
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
// returns min level of price
|
|
104
|
+
minPriceQueue = () => {
|
|
105
|
+
if (this._depthSide > 0) {
|
|
106
|
+
const min = this._side === Side.SELL ? this._priceTree.begin : this._priceTree.end;
|
|
107
|
+
return min.value;
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
// returns nearest OrderQueue with price less than given
|
|
111
|
+
lowerThan = (price) => {
|
|
112
|
+
const node = this._side === Side.SELL
|
|
113
|
+
? this._priceTree.lt(price)
|
|
114
|
+
: this._priceTree.gt(price);
|
|
115
|
+
return node.value;
|
|
116
|
+
};
|
|
117
|
+
// returns nearest OrderQueue with price greater than given
|
|
118
|
+
greaterThan = (price) => {
|
|
119
|
+
const node = this._side === Side.SELL
|
|
120
|
+
? this._priceTree.gt(price)
|
|
121
|
+
: this._priceTree.lt(price);
|
|
122
|
+
return node.value;
|
|
123
|
+
};
|
|
124
|
+
// returns all orders
|
|
125
|
+
orders = () => {
|
|
126
|
+
let orders = [];
|
|
127
|
+
for (const price in this._prices) {
|
|
128
|
+
const allOrders = this._prices[price].toArray();
|
|
129
|
+
orders = orders.concat(allOrders);
|
|
130
|
+
}
|
|
131
|
+
return orders;
|
|
132
|
+
};
|
|
133
|
+
toString = () => {
|
|
134
|
+
let s = "";
|
|
135
|
+
let level = this.maxPriceQueue();
|
|
136
|
+
while (level !== undefined) {
|
|
137
|
+
const volume = level.volume().toString();
|
|
138
|
+
s += `\n${level.price()} -> ${volume}`;
|
|
139
|
+
level = this.lowerThan(level.price());
|
|
140
|
+
}
|
|
141
|
+
return s;
|
|
142
|
+
};
|
|
143
|
+
}
|
package/dist/esm/stopbook.js
CHANGED
|
@@ -1,73 +1,72 @@
|
|
|
1
1
|
import { StopSide } from "./stopside";
|
|
2
2
|
import { OrderType, Side } from "./types";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
var stopSide = order.side === Side.BUY ? _this.bids : _this.asks;
|
|
8
|
-
stopSide.append(order);
|
|
9
|
-
};
|
|
10
|
-
this.remove = function (side, id, stopPrice) {
|
|
11
|
-
var stopSide = side === Side.BUY ? _this.bids : _this.asks;
|
|
12
|
-
return stopSide.remove(id, stopPrice);
|
|
13
|
-
};
|
|
14
|
-
this.removePriceLevel = function (side, priceLevel) {
|
|
15
|
-
var stopSide = side === Side.BUY ? _this.bids : _this.asks;
|
|
16
|
-
stopSide.removePriceLevel(priceLevel);
|
|
17
|
-
};
|
|
18
|
-
this.getConditionalOrders = function (side, priceBefore, marketPrice) {
|
|
19
|
-
var stopSide = side === Side.BUY ? _this.bids : _this.asks;
|
|
20
|
-
return stopSide.between(priceBefore, marketPrice);
|
|
21
|
-
};
|
|
22
|
-
/**
|
|
23
|
-
* Stop-Limit Order:
|
|
24
|
-
* Buy: marketPrice < stopPrice <= price
|
|
25
|
-
* Sell: marketPrice > stopPrice >= price
|
|
26
|
-
* Stop-Market Order:
|
|
27
|
-
* Buy: marketPrice < stopPrice
|
|
28
|
-
* Sell: marketPrice > stopPrice
|
|
29
|
-
*/
|
|
30
|
-
this.validConditionalOrder = function (marketPrice, order) {
|
|
31
|
-
var response = false;
|
|
32
|
-
var type = order.type, side = order.side, stopPrice = order.stopPrice;
|
|
33
|
-
if (type === OrderType.STOP_LIMIT) {
|
|
34
|
-
// Buy: marketPrice < stopPrice <= price
|
|
35
|
-
if (side === Side.BUY &&
|
|
36
|
-
marketPrice < stopPrice &&
|
|
37
|
-
stopPrice <= order.price) {
|
|
38
|
-
response = true;
|
|
39
|
-
}
|
|
40
|
-
// Sell: marketPrice > stopPrice >= price
|
|
41
|
-
if (side === Side.SELL &&
|
|
42
|
-
marketPrice > stopPrice &&
|
|
43
|
-
stopPrice >= order.price) {
|
|
44
|
-
response = true;
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
else {
|
|
48
|
-
// Buy: marketPrice < stopPrice
|
|
49
|
-
if (side === Side.BUY && marketPrice < stopPrice)
|
|
50
|
-
response = true;
|
|
51
|
-
// Sell: marketPrice > stopPrice
|
|
52
|
-
if (side === Side.SELL && marketPrice > stopPrice)
|
|
53
|
-
response = true;
|
|
54
|
-
}
|
|
55
|
-
return response;
|
|
56
|
-
};
|
|
57
|
-
this.snapshot = function () {
|
|
58
|
-
var bids = [];
|
|
59
|
-
var asks = [];
|
|
60
|
-
_this.bids.priceTree().forEach(function (price, orders) {
|
|
61
|
-
bids.push({ price: price, orders: orders.toArray().map(function (o) { return o.toObject(); }) });
|
|
62
|
-
});
|
|
63
|
-
_this.asks.priceTree().forEach(function (price, orders) {
|
|
64
|
-
asks.push({ price: price, orders: orders.toArray().map(function (o) { return o.toObject(); }) });
|
|
65
|
-
});
|
|
66
|
-
return { bids: bids, asks: asks };
|
|
67
|
-
};
|
|
3
|
+
export class StopBook {
|
|
4
|
+
bids;
|
|
5
|
+
asks;
|
|
6
|
+
constructor() {
|
|
68
7
|
this.bids = new StopSide(Side.BUY);
|
|
69
8
|
this.asks = new StopSide(Side.SELL);
|
|
70
9
|
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
10
|
+
add = (order) => {
|
|
11
|
+
const stopSide = order.side === Side.BUY ? this.bids : this.asks;
|
|
12
|
+
stopSide.append(order);
|
|
13
|
+
};
|
|
14
|
+
remove = (side, id, stopPrice) => {
|
|
15
|
+
const stopSide = side === Side.BUY ? this.bids : this.asks;
|
|
16
|
+
return stopSide.remove(id, stopPrice);
|
|
17
|
+
};
|
|
18
|
+
removePriceLevel = (side, priceLevel) => {
|
|
19
|
+
const stopSide = side === Side.BUY ? this.bids : this.asks;
|
|
20
|
+
stopSide.removePriceLevel(priceLevel);
|
|
21
|
+
};
|
|
22
|
+
getConditionalOrders = (side, priceBefore, marketPrice) => {
|
|
23
|
+
const stopSide = side === Side.BUY ? this.bids : this.asks;
|
|
24
|
+
return stopSide.between(priceBefore, marketPrice);
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Stop-Limit Order:
|
|
28
|
+
* Buy: marketPrice < stopPrice <= price
|
|
29
|
+
* Sell: marketPrice > stopPrice >= price
|
|
30
|
+
* Stop-Market Order:
|
|
31
|
+
* Buy: marketPrice < stopPrice
|
|
32
|
+
* Sell: marketPrice > stopPrice
|
|
33
|
+
*/
|
|
34
|
+
validConditionalOrder = (marketPrice, order) => {
|
|
35
|
+
let response = false;
|
|
36
|
+
const { type, side, stopPrice } = order;
|
|
37
|
+
if (type === OrderType.STOP_LIMIT) {
|
|
38
|
+
// Buy: marketPrice < stopPrice <= price
|
|
39
|
+
if (side === Side.BUY &&
|
|
40
|
+
marketPrice < stopPrice &&
|
|
41
|
+
stopPrice <= order.price) {
|
|
42
|
+
response = true;
|
|
43
|
+
}
|
|
44
|
+
// Sell: marketPrice > stopPrice >= price
|
|
45
|
+
if (side === Side.SELL &&
|
|
46
|
+
marketPrice > stopPrice &&
|
|
47
|
+
stopPrice >= order.price) {
|
|
48
|
+
response = true;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
// Buy: marketPrice < stopPrice
|
|
53
|
+
if (side === Side.BUY && marketPrice < stopPrice)
|
|
54
|
+
response = true;
|
|
55
|
+
// Sell: marketPrice > stopPrice
|
|
56
|
+
if (side === Side.SELL && marketPrice > stopPrice)
|
|
57
|
+
response = true;
|
|
58
|
+
}
|
|
59
|
+
return response;
|
|
60
|
+
};
|
|
61
|
+
snapshot = () => {
|
|
62
|
+
const bids = [];
|
|
63
|
+
const asks = [];
|
|
64
|
+
this.bids.priceTree().forEach((price, orders) => {
|
|
65
|
+
bids.push({ price, orders: orders.toArray().map((o) => o.toObject()) });
|
|
66
|
+
});
|
|
67
|
+
this.asks.priceTree().forEach((price, orders) => {
|
|
68
|
+
asks.push({ price, orders: orders.toArray().map((o) => o.toObject()) });
|
|
69
|
+
});
|
|
70
|
+
return { bids, asks };
|
|
71
|
+
};
|
|
72
|
+
}
|
package/dist/esm/stopqueue.js
CHANGED
|
@@ -1,56 +1,51 @@
|
|
|
1
1
|
/* node:coverage ignore next - Don't know why first and last line of each file count as uncovered */
|
|
2
2
|
import Denque from "denque";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
this.len = function () {
|
|
9
|
-
return _this._orders.length;
|
|
10
|
-
};
|
|
11
|
-
// remove order from head of queue
|
|
12
|
-
this.removeFromHead = function () {
|
|
13
|
-
// We can't use the shift method here because we need
|
|
14
|
-
// to update index in the map, so we use the remove(id) function
|
|
15
|
-
var order = _this._orders.peekFront();
|
|
16
|
-
if (order === undefined)
|
|
17
|
-
return;
|
|
18
|
-
return _this.remove(order.id);
|
|
19
|
-
};
|
|
20
|
-
// adds order to tail of the queue
|
|
21
|
-
this.append = function (order) {
|
|
22
|
-
_this._orders.push(order);
|
|
23
|
-
_this._ordersMap[order.id] = _this._orders.length - 1;
|
|
24
|
-
return order;
|
|
25
|
-
};
|
|
26
|
-
// removes order from the queue
|
|
27
|
-
this.remove = function (id) {
|
|
28
|
-
var deletedOrderIndex = _this._ordersMap[id];
|
|
29
|
-
if (deletedOrderIndex === undefined)
|
|
30
|
-
return;
|
|
31
|
-
var deletedOrder = _this._orders.removeOne(deletedOrderIndex);
|
|
32
|
-
delete _this._ordersMap[id];
|
|
33
|
-
// Update all orders indexes where index is greater than the deleted one
|
|
34
|
-
for (var orderId in _this._ordersMap) {
|
|
35
|
-
if (_this._ordersMap[orderId] > deletedOrderIndex) {
|
|
36
|
-
_this._ordersMap[orderId] -= 1;
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
return deletedOrder;
|
|
40
|
-
};
|
|
41
|
-
this.toArray = function () {
|
|
42
|
-
return _this._orders.toArray();
|
|
43
|
-
};
|
|
3
|
+
export class StopQueue {
|
|
4
|
+
_price;
|
|
5
|
+
_orders;
|
|
6
|
+
_ordersMap = {};
|
|
7
|
+
constructor(price) {
|
|
44
8
|
this._price = price;
|
|
45
9
|
this._orders = new Denque();
|
|
46
10
|
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
11
|
+
get price() {
|
|
12
|
+
return this._price;
|
|
13
|
+
}
|
|
14
|
+
// returns the number of orders in queue
|
|
15
|
+
len = () => {
|
|
16
|
+
return this._orders.length;
|
|
17
|
+
};
|
|
18
|
+
// remove order from head of queue
|
|
19
|
+
removeFromHead = () => {
|
|
20
|
+
// We can't use the shift method here because we need
|
|
21
|
+
// to update index in the map, so we use the remove(id) function
|
|
22
|
+
const order = this._orders.peekFront();
|
|
23
|
+
if (order === undefined)
|
|
24
|
+
return;
|
|
25
|
+
return this.remove(order.id);
|
|
26
|
+
};
|
|
27
|
+
// adds order to tail of the queue
|
|
28
|
+
append = (order) => {
|
|
29
|
+
this._orders.push(order);
|
|
30
|
+
this._ordersMap[order.id] = this._orders.length - 1;
|
|
31
|
+
return order;
|
|
32
|
+
};
|
|
33
|
+
// removes order from the queue
|
|
34
|
+
remove = (id) => {
|
|
35
|
+
const deletedOrderIndex = this._ordersMap[id];
|
|
36
|
+
if (deletedOrderIndex === undefined)
|
|
37
|
+
return;
|
|
38
|
+
const deletedOrder = this._orders.removeOne(deletedOrderIndex);
|
|
39
|
+
delete this._ordersMap[id];
|
|
40
|
+
// Update all orders indexes where index is greater than the deleted one
|
|
41
|
+
for (const orderId in this._ordersMap) {
|
|
42
|
+
if (this._ordersMap[orderId] > deletedOrderIndex) {
|
|
43
|
+
this._ordersMap[orderId] -= 1;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return deletedOrder;
|
|
47
|
+
};
|
|
48
|
+
toArray = () => {
|
|
49
|
+
return this._orders.toArray();
|
|
50
|
+
};
|
|
51
|
+
}
|