nodejs-order-book 8.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/LICENSE +21 -0
- package/README.md +464 -0
- package/dist/cjs/errors.js +81 -0
- package/dist/cjs/index.js +10 -0
- package/dist/cjs/order.js +303 -0
- package/dist/cjs/orderbook.js +850 -0
- package/dist/cjs/orderqueue.js +78 -0
- package/dist/cjs/orderside.js +158 -0
- package/dist/cjs/stopbook.js +65 -0
- package/dist/cjs/stopqueue.js +59 -0
- package/dist/cjs/stopside.js +76 -0
- package/dist/cjs/types.js +23 -0
- package/dist/esm/errors.js +77 -0
- package/dist/esm/index.js +5 -0
- package/dist/esm/order.js +300 -0
- package/dist/esm/orderbook.js +847 -0
- package/dist/esm/orderqueue.js +72 -0
- package/dist/esm/orderside.js +152 -0
- package/dist/esm/stopbook.js +62 -0
- package/dist/esm/stopqueue.js +53 -0
- package/dist/esm/stopside.js +70 -0
- package/dist/esm/types.js +20 -0
- package/dist/types/errors.d.ts +26 -0
- package/dist/types/index.d.ts +4 -0
- package/dist/types/order.d.ts +71 -0
- package/dist/types/orderbook.d.ts +218 -0
- package/dist/types/orderqueue.d.ts +18 -0
- package/dist/types/orderside.d.ts +29 -0
- package/dist/types/stopbook.d.ts +20 -0
- package/dist/types/stopqueue.d.ts +12 -0
- package/dist/types/stopside.d.ts +12 -0
- package/dist/types/types.d.ts +324 -0
- package/dist/umd/index.js +2 -0
- package/dist/umd/index.js.LICENSE.txt +10 -0
- package/package.json +89 -0
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.OrderQueue = void 0;
|
|
7
|
+
/* node:coverage ignore next - Don't know why first and last line of each file count as uncovered */
|
|
8
|
+
var denque_1 = __importDefault(require("denque"));
|
|
9
|
+
var OrderQueue = /** @class */ (function () {
|
|
10
|
+
function OrderQueue(price) {
|
|
11
|
+
var _this = this;
|
|
12
|
+
this._ordersMap = {};
|
|
13
|
+
// returns the number of orders in queue
|
|
14
|
+
this.len = function () {
|
|
15
|
+
return _this._orders.length;
|
|
16
|
+
};
|
|
17
|
+
this.toArray = function () {
|
|
18
|
+
return _this._orders.toArray();
|
|
19
|
+
};
|
|
20
|
+
// returns price level of the queue
|
|
21
|
+
this.price = function () {
|
|
22
|
+
return _this._price;
|
|
23
|
+
};
|
|
24
|
+
// returns price level of the queue
|
|
25
|
+
this.volume = function () {
|
|
26
|
+
return _this._volume;
|
|
27
|
+
};
|
|
28
|
+
// returns top order in queue
|
|
29
|
+
this.head = function () {
|
|
30
|
+
return _this._orders.peekFront();
|
|
31
|
+
};
|
|
32
|
+
// returns bottom order in queue
|
|
33
|
+
this.tail = function () {
|
|
34
|
+
return _this._orders.peekBack();
|
|
35
|
+
};
|
|
36
|
+
// adds order to tail of the queue
|
|
37
|
+
this.append = function (order) {
|
|
38
|
+
_this._volume += order.size;
|
|
39
|
+
_this._orders.push(order);
|
|
40
|
+
_this._ordersMap[order.id] = _this._orders.length - 1;
|
|
41
|
+
return order;
|
|
42
|
+
};
|
|
43
|
+
// sets up new order to list value
|
|
44
|
+
this.update = function (oldOrder, newOrder) {
|
|
45
|
+
_this._volume -= oldOrder.size;
|
|
46
|
+
_this._volume += newOrder.size;
|
|
47
|
+
// Remove old order from head
|
|
48
|
+
_this._orders.shift();
|
|
49
|
+
delete _this._ordersMap[oldOrder.id];
|
|
50
|
+
// Add new order to head
|
|
51
|
+
_this._orders.unshift(newOrder);
|
|
52
|
+
_this._ordersMap[newOrder.id] = 0;
|
|
53
|
+
};
|
|
54
|
+
// removes order from the queue
|
|
55
|
+
this.remove = function (order) {
|
|
56
|
+
_this._volume -= order.size;
|
|
57
|
+
var deletedOrderIndex = _this._ordersMap[order.id];
|
|
58
|
+
_this._orders.removeOne(deletedOrderIndex);
|
|
59
|
+
delete _this._ordersMap[order.id];
|
|
60
|
+
// Update all orders indexes where index is greater than the deleted one
|
|
61
|
+
for (var orderId in _this._ordersMap) {
|
|
62
|
+
if (_this._ordersMap[orderId] > deletedOrderIndex) {
|
|
63
|
+
_this._ordersMap[orderId] -= 1;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
this.updateOrderSize = function (order, size) {
|
|
68
|
+
_this._volume += size - order.size; // update volume
|
|
69
|
+
order.size = size;
|
|
70
|
+
order.time = Date.now();
|
|
71
|
+
};
|
|
72
|
+
this._price = price;
|
|
73
|
+
this._volume = 0;
|
|
74
|
+
this._orders = new denque_1.default();
|
|
75
|
+
}
|
|
76
|
+
return OrderQueue;
|
|
77
|
+
}());
|
|
78
|
+
exports.OrderQueue = OrderQueue;
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __assign = (this && this.__assign) || function () {
|
|
3
|
+
__assign = Object.assign || function(t) {
|
|
4
|
+
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
5
|
+
s = arguments[i];
|
|
6
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
|
7
|
+
t[p] = s[p];
|
|
8
|
+
}
|
|
9
|
+
return t;
|
|
10
|
+
};
|
|
11
|
+
return __assign.apply(this, arguments);
|
|
12
|
+
};
|
|
13
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
14
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.OrderSide = void 0;
|
|
18
|
+
/* node:coverage ignore next - Don't know why first and last line of each file count as uncovered */
|
|
19
|
+
var functional_red_black_tree_1 = __importDefault(require("functional-red-black-tree"));
|
|
20
|
+
var errors_1 = require("./errors");
|
|
21
|
+
var order_1 = require("./order");
|
|
22
|
+
var orderqueue_1 = require("./orderqueue");
|
|
23
|
+
var types_1 = require("./types");
|
|
24
|
+
var OrderSide = /** @class */ (function () {
|
|
25
|
+
function OrderSide(side) {
|
|
26
|
+
var _this = this;
|
|
27
|
+
this._prices = {};
|
|
28
|
+
this._volume = 0;
|
|
29
|
+
this._total = 0;
|
|
30
|
+
this._numOrders = 0;
|
|
31
|
+
this._depthSide = 0;
|
|
32
|
+
this._side = types_1.Side.SELL;
|
|
33
|
+
// returns amount of orders
|
|
34
|
+
this.len = function () {
|
|
35
|
+
return _this._numOrders;
|
|
36
|
+
};
|
|
37
|
+
// returns depth of market
|
|
38
|
+
this.depth = function () {
|
|
39
|
+
return _this._depthSide;
|
|
40
|
+
};
|
|
41
|
+
// returns total amount of quantity in side
|
|
42
|
+
this.volume = function () {
|
|
43
|
+
return _this._volume;
|
|
44
|
+
};
|
|
45
|
+
// returns the total (size * price of each price level) in side
|
|
46
|
+
this.total = function () {
|
|
47
|
+
return _this._total;
|
|
48
|
+
};
|
|
49
|
+
// returns the price tree in side
|
|
50
|
+
this.priceTree = function () {
|
|
51
|
+
return _this._priceTree;
|
|
52
|
+
};
|
|
53
|
+
// appends order to definite price level
|
|
54
|
+
this.append = function (order) {
|
|
55
|
+
var price = order.price;
|
|
56
|
+
var strPrice = price.toString();
|
|
57
|
+
if (_this._prices[strPrice] === undefined) {
|
|
58
|
+
var priceQueue = new orderqueue_1.OrderQueue(price);
|
|
59
|
+
_this._prices[strPrice] = priceQueue;
|
|
60
|
+
_this._priceTree = _this._priceTree.insert(price, priceQueue);
|
|
61
|
+
_this._depthSide += 1;
|
|
62
|
+
}
|
|
63
|
+
_this._numOrders += 1;
|
|
64
|
+
_this._volume += order.size;
|
|
65
|
+
_this._total += order.size * order.price;
|
|
66
|
+
return _this._prices[strPrice].append(order);
|
|
67
|
+
};
|
|
68
|
+
// removes order from definite price level
|
|
69
|
+
this.remove = function (order) {
|
|
70
|
+
var price = order.price;
|
|
71
|
+
var strPrice = price.toString();
|
|
72
|
+
if (_this._prices[strPrice] === undefined) {
|
|
73
|
+
throw (0, errors_1.CustomError)(errors_1.ERROR.INVALID_PRICE_LEVEL);
|
|
74
|
+
}
|
|
75
|
+
_this._prices[strPrice].remove(order);
|
|
76
|
+
if (_this._prices[strPrice].len() === 0) {
|
|
77
|
+
delete _this._prices[strPrice];
|
|
78
|
+
_this._priceTree = _this._priceTree.remove(price);
|
|
79
|
+
_this._depthSide -= 1;
|
|
80
|
+
}
|
|
81
|
+
_this._numOrders -= 1;
|
|
82
|
+
_this._volume -= order.size;
|
|
83
|
+
_this._total -= order.size * order.price;
|
|
84
|
+
return order;
|
|
85
|
+
};
|
|
86
|
+
// Update the price of an order and return the order with the updated price
|
|
87
|
+
this.updateOrderPrice = function (oldOrder, orderUpdate) {
|
|
88
|
+
_this.remove(oldOrder);
|
|
89
|
+
var newOrder = order_1.OrderFactory.createOrder(__assign(__assign({}, oldOrder.toObject()), { size: orderUpdate.size !== undefined ? orderUpdate.size : oldOrder.size, price: orderUpdate.price, time: Date.now() }));
|
|
90
|
+
_this.append(newOrder);
|
|
91
|
+
return newOrder;
|
|
92
|
+
};
|
|
93
|
+
// Update the price of an order and return the order with the updated price
|
|
94
|
+
this.updateOrderSize = function (oldOrder, orderUpdate) {
|
|
95
|
+
var _a;
|
|
96
|
+
var newOrderPrice = (_a = orderUpdate.price) !== null && _a !== void 0 ? _a : oldOrder.price;
|
|
97
|
+
_this._volume += orderUpdate.size - oldOrder.size;
|
|
98
|
+
_this._total +=
|
|
99
|
+
orderUpdate.size * newOrderPrice - oldOrder.size * oldOrder.price;
|
|
100
|
+
_this._prices[oldOrder.price.toString()].updateOrderSize(oldOrder, orderUpdate.size);
|
|
101
|
+
return oldOrder;
|
|
102
|
+
};
|
|
103
|
+
// returns max level of price
|
|
104
|
+
this.maxPriceQueue = function () {
|
|
105
|
+
if (_this._depthSide > 0) {
|
|
106
|
+
var max = _this._side === types_1.Side.SELL ? _this._priceTree.end : _this._priceTree.begin;
|
|
107
|
+
return max.value;
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
// returns min level of price
|
|
111
|
+
this.minPriceQueue = function () {
|
|
112
|
+
if (_this._depthSide > 0) {
|
|
113
|
+
var min = _this._side === types_1.Side.SELL ? _this._priceTree.begin : _this._priceTree.end;
|
|
114
|
+
return min.value;
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
// returns nearest OrderQueue with price less than given
|
|
118
|
+
this.lowerThan = function (price) {
|
|
119
|
+
var node = _this._side === types_1.Side.SELL
|
|
120
|
+
? _this._priceTree.lt(price)
|
|
121
|
+
: _this._priceTree.gt(price);
|
|
122
|
+
return node.value;
|
|
123
|
+
};
|
|
124
|
+
// returns nearest OrderQueue with price greater than given
|
|
125
|
+
this.greaterThan = function (price) {
|
|
126
|
+
var node = _this._side === types_1.Side.SELL
|
|
127
|
+
? _this._priceTree.gt(price)
|
|
128
|
+
: _this._priceTree.lt(price);
|
|
129
|
+
return node.value;
|
|
130
|
+
};
|
|
131
|
+
// returns all orders
|
|
132
|
+
this.orders = function () {
|
|
133
|
+
var orders = [];
|
|
134
|
+
for (var price in _this._prices) {
|
|
135
|
+
var allOrders = _this._prices[price].toArray();
|
|
136
|
+
orders = orders.concat(allOrders);
|
|
137
|
+
}
|
|
138
|
+
return orders;
|
|
139
|
+
};
|
|
140
|
+
this.toString = function () {
|
|
141
|
+
var s = "";
|
|
142
|
+
var level = _this.maxPriceQueue();
|
|
143
|
+
while (level !== undefined) {
|
|
144
|
+
var volume = level.volume().toString();
|
|
145
|
+
s += "\n".concat(level.price(), " -> ").concat(volume);
|
|
146
|
+
level = _this.lowerThan(level.price());
|
|
147
|
+
}
|
|
148
|
+
return s;
|
|
149
|
+
};
|
|
150
|
+
var compare = side === types_1.Side.SELL
|
|
151
|
+
? function (a, b) { return a - b; }
|
|
152
|
+
: function (a, b) { return b - a; };
|
|
153
|
+
this._priceTree = (0, functional_red_black_tree_1.default)(compare);
|
|
154
|
+
this._side = side;
|
|
155
|
+
}
|
|
156
|
+
return OrderSide;
|
|
157
|
+
}());
|
|
158
|
+
exports.OrderSide = OrderSide;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.StopBook = void 0;
|
|
4
|
+
var stopside_1 = require("./stopside");
|
|
5
|
+
var types_1 = require("./types");
|
|
6
|
+
var StopBook = /** @class */ (function () {
|
|
7
|
+
function StopBook() {
|
|
8
|
+
var _this = this;
|
|
9
|
+
this.add = function (order) {
|
|
10
|
+
var stopSide = order.side === types_1.Side.BUY ? _this.bids : _this.asks;
|
|
11
|
+
stopSide.append(order);
|
|
12
|
+
};
|
|
13
|
+
this.remove = function (side, id, stopPrice) {
|
|
14
|
+
var stopSide = side === types_1.Side.BUY ? _this.bids : _this.asks;
|
|
15
|
+
return stopSide.remove(id, stopPrice);
|
|
16
|
+
};
|
|
17
|
+
this.removePriceLevel = function (side, priceLevel) {
|
|
18
|
+
var stopSide = side === types_1.Side.BUY ? _this.bids : _this.asks;
|
|
19
|
+
stopSide.removePriceLevel(priceLevel);
|
|
20
|
+
};
|
|
21
|
+
this.getConditionalOrders = function (side, priceBefore, marketPrice) {
|
|
22
|
+
var stopSide = side === types_1.Side.BUY ? _this.bids : _this.asks;
|
|
23
|
+
return stopSide.between(priceBefore, marketPrice);
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* Stop-Limit Order:
|
|
27
|
+
* Buy: marketPrice < stopPrice <= price
|
|
28
|
+
* Sell: marketPrice > stopPrice >= price
|
|
29
|
+
* Stop-Market Order:
|
|
30
|
+
* Buy: marketPrice < stopPrice
|
|
31
|
+
* Sell: marketPrice > stopPrice
|
|
32
|
+
*/
|
|
33
|
+
this.validConditionalOrder = function (marketPrice, order) {
|
|
34
|
+
var response = false;
|
|
35
|
+
var type = order.type, side = order.side, stopPrice = order.stopPrice;
|
|
36
|
+
if (type === types_1.OrderType.STOP_LIMIT) {
|
|
37
|
+
// Buy: marketPrice < stopPrice <= price
|
|
38
|
+
if (side === types_1.Side.BUY &&
|
|
39
|
+
marketPrice < stopPrice &&
|
|
40
|
+
stopPrice <= order.price) {
|
|
41
|
+
response = true;
|
|
42
|
+
}
|
|
43
|
+
// Sell: marketPrice > stopPrice >= price
|
|
44
|
+
if (side === types_1.Side.SELL &&
|
|
45
|
+
marketPrice > stopPrice &&
|
|
46
|
+
stopPrice >= order.price) {
|
|
47
|
+
response = true;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
// Buy: marketPrice < stopPrice
|
|
52
|
+
if (side === types_1.Side.BUY && marketPrice < stopPrice)
|
|
53
|
+
response = true;
|
|
54
|
+
// Sell: marketPrice > stopPrice
|
|
55
|
+
if (side === types_1.Side.SELL && marketPrice > stopPrice)
|
|
56
|
+
response = true;
|
|
57
|
+
}
|
|
58
|
+
return response;
|
|
59
|
+
};
|
|
60
|
+
this.bids = new stopside_1.StopSide(types_1.Side.BUY);
|
|
61
|
+
this.asks = new stopside_1.StopSide(types_1.Side.SELL);
|
|
62
|
+
}
|
|
63
|
+
return StopBook;
|
|
64
|
+
}());
|
|
65
|
+
exports.StopBook = StopBook;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.StopQueue = void 0;
|
|
7
|
+
/* node:coverage ignore next - Don't know why first and last line of each file count as uncovered */
|
|
8
|
+
var denque_1 = __importDefault(require("denque"));
|
|
9
|
+
var StopQueue = /** @class */ (function () {
|
|
10
|
+
function StopQueue(price) {
|
|
11
|
+
var _this = this;
|
|
12
|
+
this._ordersMap = {};
|
|
13
|
+
// returns the number of orders in queue
|
|
14
|
+
this.len = function () {
|
|
15
|
+
return _this._orders.length;
|
|
16
|
+
};
|
|
17
|
+
// remove order from head of queue
|
|
18
|
+
this.removeFromHead = function () {
|
|
19
|
+
// We can't use the shift method here because we need
|
|
20
|
+
// to update index in the map, so we use the remove(id) function
|
|
21
|
+
var order = _this._orders.peekFront();
|
|
22
|
+
if (order === undefined)
|
|
23
|
+
return;
|
|
24
|
+
return _this.remove(order.id);
|
|
25
|
+
};
|
|
26
|
+
// adds order to tail of the queue
|
|
27
|
+
this.append = function (order) {
|
|
28
|
+
_this._orders.push(order);
|
|
29
|
+
_this._ordersMap[order.id] = _this._orders.length - 1;
|
|
30
|
+
return order;
|
|
31
|
+
};
|
|
32
|
+
// removes order from the queue
|
|
33
|
+
this.remove = function (id) {
|
|
34
|
+
var deletedOrderIndex = _this._ordersMap[id];
|
|
35
|
+
if (deletedOrderIndex === undefined)
|
|
36
|
+
return;
|
|
37
|
+
var deletedOrder = _this._orders.removeOne(deletedOrderIndex);
|
|
38
|
+
delete _this._ordersMap[id];
|
|
39
|
+
// Update all orders indexes where index is greater than the deleted one
|
|
40
|
+
for (var orderId in _this._ordersMap) {
|
|
41
|
+
if (_this._ordersMap[orderId] > deletedOrderIndex) {
|
|
42
|
+
_this._ordersMap[orderId] -= 1;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return deletedOrder;
|
|
46
|
+
};
|
|
47
|
+
this._price = price;
|
|
48
|
+
this._orders = new denque_1.default();
|
|
49
|
+
}
|
|
50
|
+
Object.defineProperty(StopQueue.prototype, "price", {
|
|
51
|
+
get: function () {
|
|
52
|
+
return this._price;
|
|
53
|
+
},
|
|
54
|
+
enumerable: false,
|
|
55
|
+
configurable: true
|
|
56
|
+
});
|
|
57
|
+
return StopQueue;
|
|
58
|
+
}());
|
|
59
|
+
exports.StopQueue = StopQueue;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.StopSide = void 0;
|
|
7
|
+
/* node:coverage ignore next - Don't know why first and last line of each file count as uncovered */
|
|
8
|
+
var functional_red_black_tree_1 = __importDefault(require("functional-red-black-tree"));
|
|
9
|
+
var errors_1 = require("./errors");
|
|
10
|
+
var stopqueue_1 = require("./stopqueue");
|
|
11
|
+
var types_1 = require("./types");
|
|
12
|
+
var StopSide = /** @class */ (function () {
|
|
13
|
+
function StopSide(side) {
|
|
14
|
+
var _this = this;
|
|
15
|
+
this._prices = {};
|
|
16
|
+
// appends order to definite price level
|
|
17
|
+
this.append = function (order) {
|
|
18
|
+
var price = order.stopPrice;
|
|
19
|
+
var strPrice = price.toString();
|
|
20
|
+
if (_this._prices[strPrice] === undefined) {
|
|
21
|
+
var priceQueue = new stopqueue_1.StopQueue(price);
|
|
22
|
+
_this._prices[strPrice] = priceQueue;
|
|
23
|
+
_this._priceTree = _this._priceTree.insert(price, priceQueue);
|
|
24
|
+
}
|
|
25
|
+
return _this._prices[strPrice].append(order);
|
|
26
|
+
};
|
|
27
|
+
// removes order from definite price level
|
|
28
|
+
this.remove = function (id, stopPrice) {
|
|
29
|
+
var strPrice = stopPrice.toString();
|
|
30
|
+
if (_this._prices[strPrice] === undefined) {
|
|
31
|
+
throw (0, errors_1.CustomError)(errors_1.ERROR.INVALID_PRICE_LEVEL);
|
|
32
|
+
}
|
|
33
|
+
var deletedOrder = _this._prices[strPrice].remove(id);
|
|
34
|
+
if (_this._prices[strPrice].len() === 0) {
|
|
35
|
+
delete _this._prices[strPrice];
|
|
36
|
+
_this._priceTree = _this._priceTree.remove(stopPrice);
|
|
37
|
+
}
|
|
38
|
+
return deletedOrder;
|
|
39
|
+
};
|
|
40
|
+
this.removePriceLevel = function (priceLevel) {
|
|
41
|
+
delete _this._prices[priceLevel.toString()];
|
|
42
|
+
_this._priceTree = _this._priceTree.remove(priceLevel);
|
|
43
|
+
};
|
|
44
|
+
// Get orders queue between two price levels
|
|
45
|
+
this.between = function (priceBefore, marketPrice) {
|
|
46
|
+
var queues = [];
|
|
47
|
+
var lowerBound = priceBefore;
|
|
48
|
+
var upperBound = marketPrice;
|
|
49
|
+
var highest = Math.max(priceBefore, marketPrice);
|
|
50
|
+
var lowest = Math.min(priceBefore, marketPrice);
|
|
51
|
+
if (_this._side === types_1.Side.BUY) {
|
|
52
|
+
lowerBound = highest;
|
|
53
|
+
upperBound = lowest - 1;
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
lowerBound = lowest;
|
|
57
|
+
upperBound = highest + 1;
|
|
58
|
+
}
|
|
59
|
+
_this._priceTree.forEach(function (price, queue) {
|
|
60
|
+
if ((_this._side === types_1.Side.BUY && price >= lowest) ||
|
|
61
|
+
(_this._side === types_1.Side.SELL && price <= highest)) {
|
|
62
|
+
queues.push(queue);
|
|
63
|
+
}
|
|
64
|
+
}, lowerBound, // Inclusive
|
|
65
|
+
upperBound);
|
|
66
|
+
return queues;
|
|
67
|
+
};
|
|
68
|
+
var compare = side === types_1.Side.SELL
|
|
69
|
+
? function (a, b) { return a - b; }
|
|
70
|
+
: function (a, b) { return b - a; };
|
|
71
|
+
this._priceTree = (0, functional_red_black_tree_1.default)(compare);
|
|
72
|
+
this._side = side;
|
|
73
|
+
}
|
|
74
|
+
return StopSide;
|
|
75
|
+
}());
|
|
76
|
+
exports.StopSide = StopSide;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TimeInForce = exports.OrderType = exports.Side = void 0;
|
|
4
|
+
var Side;
|
|
5
|
+
(function (Side) {
|
|
6
|
+
Side["BUY"] = "buy";
|
|
7
|
+
Side["SELL"] = "sell";
|
|
8
|
+
})(Side || (exports.Side = Side = {}));
|
|
9
|
+
var OrderType;
|
|
10
|
+
(function (OrderType) {
|
|
11
|
+
OrderType["LIMIT"] = "limit";
|
|
12
|
+
OrderType["MARKET"] = "market";
|
|
13
|
+
OrderType["OCO"] = "oco";
|
|
14
|
+
OrderType["STOP_LIMIT"] = "stop_limit";
|
|
15
|
+
OrderType["STOP_MARKET"] = "stop_market";
|
|
16
|
+
})(OrderType || (exports.OrderType = OrderType = {}));
|
|
17
|
+
var TimeInForce;
|
|
18
|
+
(function (TimeInForce) {
|
|
19
|
+
TimeInForce["GTC"] = "GTC";
|
|
20
|
+
TimeInForce["IOC"] = "IOC";
|
|
21
|
+
TimeInForce["FOK"] = "FOK";
|
|
22
|
+
})(TimeInForce || (exports.TimeInForce = TimeInForce = {}));
|
|
23
|
+
/* node:coverage enable */
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
var _a, _b;
|
|
2
|
+
export var ERROR;
|
|
3
|
+
(function (ERROR) {
|
|
4
|
+
ERROR["DEFAULT"] = "DEFAULT";
|
|
5
|
+
ERROR["INSUFFICIENT_QUANTITY"] = "INSUFFICIENT_QUANTITY";
|
|
6
|
+
ERROR["INVALID_CONDITIONAL_ORDER"] = "INVALID_CONDITIONAL_ORDER";
|
|
7
|
+
ERROR["INVALID_JOURNAL_LOG"] = "INVALID_JOURNAL_LOG";
|
|
8
|
+
ERROR["INVALID_ORDER_TYPE"] = "INVALID_ORDER_TYPE";
|
|
9
|
+
ERROR["INVALID_PRICE"] = "INVALID_PRICE";
|
|
10
|
+
ERROR["INVALID_PRICE_LEVEL"] = "INVALID_PRICE_LEVEL";
|
|
11
|
+
ERROR["INVALID_PRICE_OR_QUANTITY"] = "INVALID_PRICE_OR_QUANTITY";
|
|
12
|
+
ERROR["INVALID_QUANTITY"] = "INVALID_QUANTITY";
|
|
13
|
+
ERROR["INVALID_SIDE"] = "INVALID_SIDE";
|
|
14
|
+
ERROR["INVALID_TIF"] = "INVALID_TIF";
|
|
15
|
+
ERROR["LIMIT_ORDER_FOK_NOT_FILLABLE"] = "LIMIT_ORDER_FOK_NOT_FILLABLE";
|
|
16
|
+
ERROR["LIMIT_ORDER_POST_ONLY"] = "LIMIT_ORDER_POST_ONLY";
|
|
17
|
+
ERROR["ORDER_ALREDY_EXISTS"] = "ORDER_ALREDY_EXISTS";
|
|
18
|
+
ERROR["ORDER_NOT_FOUND"] = "ORDER_NOT_FOUND";
|
|
19
|
+
})(ERROR || (ERROR = {}));
|
|
20
|
+
export var ErrorCodes = (_a = {},
|
|
21
|
+
// 10xx General issues
|
|
22
|
+
_a[ERROR.DEFAULT] = 1000,
|
|
23
|
+
// 11xx Request issues
|
|
24
|
+
_a[ERROR.INVALID_ORDER_TYPE] = 1100,
|
|
25
|
+
_a[ERROR.INVALID_SIDE] = 1101,
|
|
26
|
+
_a[ERROR.INVALID_QUANTITY] = 1102,
|
|
27
|
+
_a[ERROR.INVALID_PRICE] = 1103,
|
|
28
|
+
_a[ERROR.INVALID_PRICE_OR_QUANTITY] = 1104,
|
|
29
|
+
_a[ERROR.INVALID_TIF] = 1105,
|
|
30
|
+
_a[ERROR.LIMIT_ORDER_FOK_NOT_FILLABLE] = 1106,
|
|
31
|
+
_a[ERROR.LIMIT_ORDER_POST_ONLY] = 1107,
|
|
32
|
+
_a[ERROR.INVALID_CONDITIONAL_ORDER] = 1108,
|
|
33
|
+
_a[ERROR.ORDER_ALREDY_EXISTS] = 1109,
|
|
34
|
+
_a[ERROR.ORDER_NOT_FOUND] = 1110,
|
|
35
|
+
// 12xx Internal error
|
|
36
|
+
_a[ERROR.INSUFFICIENT_QUANTITY] = 1200,
|
|
37
|
+
_a[ERROR.INVALID_PRICE_LEVEL] = 1201,
|
|
38
|
+
_a[ERROR.INVALID_JOURNAL_LOG] = 1201,
|
|
39
|
+
_a);
|
|
40
|
+
export var ErrorMessages = (_b = {},
|
|
41
|
+
_b[ERROR.DEFAULT] = "Something wrong",
|
|
42
|
+
_b[ERROR.INSUFFICIENT_QUANTITY] = "Insufficient quantity to calculate price",
|
|
43
|
+
_b[ERROR.INVALID_CONDITIONAL_ORDER] = "Stop-Limit Order (BUY: marketPrice < stopPrice <= price, SELL: marketPrice > stopPrice >= price). Stop-Market Order (BUY: marketPrice < stopPrice, SELL: marketPrice > stopPrice). OCO order (BUY: price < marketPrice < stopPrice, SELL: price > marketPrice > stopPrice)",
|
|
44
|
+
_b[ERROR.INVALID_ORDER_TYPE] = "Supported order type are 'limit' and 'market'",
|
|
45
|
+
_b[ERROR.INVALID_PRICE] = "Invalid order price",
|
|
46
|
+
_b[ERROR.INVALID_PRICE_LEVEL] = "Invalid order price level",
|
|
47
|
+
_b[ERROR.INVALID_PRICE_OR_QUANTITY] = "Invalid order price or quantity",
|
|
48
|
+
_b[ERROR.INVALID_QUANTITY] = "Invalid order quantity",
|
|
49
|
+
_b[ERROR.INVALID_SIDE] = "Invalid side: must be either 'sell' or 'buy'",
|
|
50
|
+
_b[ERROR.INVALID_TIF] = "Invalid TimeInForce: must be one of 'GTC', 'IOC' or 'FOK'",
|
|
51
|
+
_b[ERROR.LIMIT_ORDER_FOK_NOT_FILLABLE] = "Limit FOK order not fillable",
|
|
52
|
+
_b[ERROR.LIMIT_ORDER_POST_ONLY] = "Post-only limit order rejected because would execute immediately",
|
|
53
|
+
_b[ERROR.ORDER_ALREDY_EXISTS] = "Order already exists",
|
|
54
|
+
_b[ERROR.ORDER_NOT_FOUND] = "Order not found",
|
|
55
|
+
_b[ERROR.INVALID_JOURNAL_LOG] = "Invalid journal log format",
|
|
56
|
+
_b);
|
|
57
|
+
var OrderBookError = /** @class */ (function () {
|
|
58
|
+
function OrderBookError(error) {
|
|
59
|
+
var _a;
|
|
60
|
+
var errorMessage;
|
|
61
|
+
if (error != null && ErrorMessages[error] != null) {
|
|
62
|
+
errorMessage = ErrorMessages[error];
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
var customMessage = error === undefined || error === "" ? "" : ": ".concat(error);
|
|
66
|
+
errorMessage = "".concat(ErrorMessages.DEFAULT).concat(customMessage);
|
|
67
|
+
}
|
|
68
|
+
this.message = errorMessage;
|
|
69
|
+
this.code = (_a = ErrorCodes[error]) !== null && _a !== void 0 ? _a : ErrorCodes[ERROR.DEFAULT];
|
|
70
|
+
}
|
|
71
|
+
return OrderBookError;
|
|
72
|
+
}());
|
|
73
|
+
export { OrderBookError };
|
|
74
|
+
/* node:coverage ignore next - Don't know why this line is uncoverd */
|
|
75
|
+
export var CustomError = function (error) {
|
|
76
|
+
return new OrderBookError(error);
|
|
77
|
+
};
|