nodejs-order-book
Version:
Node.js Lmit Order Book for high-frequency trading (HFT).
159 lines (158 loc) • 6.61 kB
JavaScript
"use strict";
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.OrderSide = void 0;
/* node:coverage ignore next - Don't know why first and last line of each file count as uncovered */
var functional_red_black_tree_1 = __importDefault(require("functional-red-black-tree"));
var errors_1 = require("./errors");
var order_1 = require("./order");
var orderqueue_1 = require("./orderqueue");
var types_1 = require("./types");
var OrderSide = /** @class */ (function () {
function OrderSide(side) {
var _this = this;
this._prices = {};
this._volume = 0;
this._total = 0;
this._numOrders = 0;
this._depthSide = 0;
this._side = types_1.Side.SELL;
// returns amount of orders
this.len = function () {
return _this._numOrders;
};
// returns depth of market
this.depth = function () {
return _this._depthSide;
};
// returns total amount of quantity in side
this.volume = function () {
return _this._volume;
};
// returns the total (size * price of each price level) in side
this.total = function () {
return _this._total;
};
// returns the price tree in side
this.priceTree = function () {
return _this._priceTree;
};
// appends order to definite price level
this.append = function (order) {
var price = order.price;
var strPrice = price.toString();
if (_this._prices[strPrice] === undefined) {
var priceQueue = new orderqueue_1.OrderQueue(price);
_this._prices[strPrice] = priceQueue;
_this._priceTree = _this._priceTree.insert(price, priceQueue);
_this._depthSide += 1;
}
_this._numOrders += 1;
_this._volume += order.size;
_this._total += order.size * order.price;
return _this._prices[strPrice].append(order);
};
// removes order from definite price level
this.remove = function (order) {
var price = order.price;
var strPrice = price.toString();
if (_this._prices[strPrice] === undefined) {
throw (0, errors_1.CustomError)(errors_1.ERROR.INVALID_PRICE_LEVEL);
}
_this._prices[strPrice].remove(order);
if (_this._prices[strPrice].len() === 0) {
delete _this._prices[strPrice];
_this._priceTree = _this._priceTree.remove(price);
_this._depthSide -= 1;
}
_this._numOrders -= 1;
_this._volume -= order.size;
_this._total -= order.size * order.price;
return order;
};
// Update the price of an order and return the order with the updated price
this.updateOrderPrice = function (oldOrder, orderUpdate) {
_this.remove(oldOrder);
var newOrder = order_1.OrderFactory.createOrder(__assign(__assign({}, oldOrder.toObject()), { size: orderUpdate.size !== undefined ? orderUpdate.size : oldOrder.size, price: orderUpdate.price, time: Date.now() }));
_this.append(newOrder);
return newOrder;
};
// Update the price of an order and return the order with the updated price
this.updateOrderSize = function (oldOrder, orderUpdate) {
var _a;
var newOrderPrice = (_a = orderUpdate.price) !== null && _a !== void 0 ? _a : oldOrder.price;
_this._volume += orderUpdate.size - oldOrder.size;
_this._total +=
orderUpdate.size * newOrderPrice - oldOrder.size * oldOrder.price;
_this._prices[oldOrder.price.toString()].updateOrderSize(oldOrder, orderUpdate.size);
return oldOrder;
};
// returns max level of price
this.maxPriceQueue = function () {
if (_this._depthSide > 0) {
var max = _this._side === types_1.Side.SELL ? _this._priceTree.end : _this._priceTree.begin;
return max.value;
}
};
// returns min level of price
this.minPriceQueue = function () {
if (_this._depthSide > 0) {
var min = _this._side === types_1.Side.SELL ? _this._priceTree.begin : _this._priceTree.end;
return min.value;
}
};
// returns nearest OrderQueue with price less than given
this.lowerThan = function (price) {
var node = _this._side === types_1.Side.SELL
? _this._priceTree.lt(price)
: _this._priceTree.gt(price);
return node.value;
};
// returns nearest OrderQueue with price greater than given
this.greaterThan = function (price) {
var node = _this._side === types_1.Side.SELL
? _this._priceTree.gt(price)
: _this._priceTree.lt(price);
return node.value;
};
// returns all orders
this.orders = function () {
var orders = [];
for (var price in _this._prices) {
var allOrders = _this._prices[price].toArray();
orders = orders.concat(allOrders);
}
return orders;
};
this.toString = function () {
var s = "";
var level = _this.maxPriceQueue();
while (level !== undefined) {
var volume = level.volume().toString();
s += "\n".concat(level.price(), " -> ").concat(volume);
level = _this.lowerThan(level.price());
}
return s;
};
var compare = side === types_1.Side.SELL
? function (a, b) { return a - b; }
: function (a, b) { return b - a; };
this._priceTree = (0, functional_red_black_tree_1.default)(compare);
this._side = side;
}
return OrderSide;
}());
exports.OrderSide = OrderSide;