UNPKG

@stoqey/ibkr

Version:

NodeJS Interactive Brokers wrapper & utilities using @stoqey/ib

395 lines 22.4 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __generator = (this && this.__generator) || function (thisArg, body) { var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype); return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (g && (g = 0, op[0] && (_ = 0)), _) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.Orders = void 0; var rxjs_1 = require("rxjs"); var ib_1 = require("@stoqey/ib"); var IBKRConnection_1 = __importStar(require("../connection/IBKRConnection")); var compact_1 = __importDefault(require("lodash/compact")); var identity_1 = __importDefault(require("lodash/identity")); var omit_1 = __importDefault(require("lodash/omit")); var pickBy_1 = __importDefault(require("lodash/pickBy")); var log_1 = require("../utils/log"); var awaitP_1 = __importDefault(require("../utils/awaitP")); var Portfolios_1 = __importDefault(require("../portfolios/Portfolios")); var instrument_utils_1 = require("../utils/instrument.utils"); var async_mutex_1 = require("async-mutex"); var log_utils_1 = require("../utils/log.utils"); var events_1 = require("../events"); var ibkrEvents = events_1.IBKREvents.Instance; var Orders = /** @class */ (function () { function Orders() { var _this = this; this.ib = null; this.openOrders = new Map(); this.cancelledOrders = new Map(); this.completedTrades = new Map(); this.openOrderQueue = []; this.mutex = new async_mutex_1.Mutex(); /** * getOrders */ this.getOrders = function () { return _this.asyncOpenOrders(); }; this.logOpenOrder = function (title, openOrder) { var _a, _b, _c, _d; try { var contract = openOrder.contract, order = openOrder.order; var symbol = (0, instrument_utils_1.getSymbolKey)(contract); var _e = order || {}, _f = _e.permId, permId = _f === void 0 ? "" : _f, _g = _e.action, action = _g === void 0 ? "" : _g, _h = _e.totalQuantity, totalQuantity = _h === void 0 ? 0 : _h, _j = _e.orderType, orderType = _j === void 0 ? "" : _j, lmtPrice = _e.lmtPrice, auxPrice = _e.auxPrice; var avgFillPrice = (_a = openOrder.orderStatus) === null || _a === void 0 ? void 0 : _a.avgFillPrice; var status = (_b = openOrder.orderStatus) === null || _b === void 0 ? void 0 : _b.status; // TODO: Print order price based on type since stop-market orders have `lmtPrice: 0` and limit orders have `auxPrice: 0` (which is valid for options though). (0, log_1.log)("Orders.".concat(title, " symbol=").concat(symbol, " permId=").concat(permId), "".concat(action, " ").concat(totalQuantity, " ").concat(orderType, " @").concat((_d = (_c = avgFillPrice !== null && avgFillPrice !== void 0 ? avgFillPrice : lmtPrice) !== null && _c !== void 0 ? _c : auxPrice) !== null && _d !== void 0 ? _d : 0, " => ").concat(status)); } catch (error) { } }; this.processOrderQueue = function () { return __awaiter(_this, void 0, void 0, function () { var portfoliosManager; var _this = this; return __generator(this, function (_a) { switch (_a.label) { case 0: if ((0, IBKRConnection_1.isMarketDataOnly)()) { (0, log_1.log)("Orders.processOrderQueue", "MD_ONLY enabled, skipping order queue processing"); return [2 /*return*/]; } portfoliosManager = Portfolios_1.default.Instance; return [4 /*yield*/, portfoliosManager.init()]; case 1: _a.sent(); return [4 /*yield*/, this.mutex.runExclusive(function () { return __awaiter(_this, void 0, void 0, function () { var order, contractId, permId, orderStatus, entryPrice, entryDate, trade; var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k; return __generator(this, function (_l) { while (this.openOrderQueue.length > 0) { order = this.openOrderQueue.shift(); contractId = (_a = order === null || order === void 0 ? void 0 : order.contract) === null || _a === void 0 ? void 0 : _a.conId; permId = (_b = order === null || order === void 0 ? void 0 : order.order) === null || _b === void 0 ? void 0 : _b.permId; // Ignore if already Filled processed / completed if (this.completedTrades.has(permId)) { continue; } ; // Ignore cancelled orders if (this.cancelledOrders.has(permId)) { continue; } this.logOpenOrder("processOrderQueue", order); orderStatus = ((_c = order.orderState) === null || _c === void 0 ? void 0 : _c.status) || ((_d = order.orderStatus) === null || _d === void 0 ? void 0 : _d.status); // log(`Orders.syncOpenOrders`, `Order ${order.permId} for contract ${orderStatus}`); switch (orderStatus) { case ib_1.OrderStatus.Filled: entryPrice = (_g = (_e = portfoliosManager.getEntryPrice(contractId)) !== null && _e !== void 0 ? _e : (_f = portfoliosManager.getLatestClosedPosition(contractId)) === null || _f === void 0 ? void 0 : _f.avgCost) !== null && _g !== void 0 ? _g : order.orderStatus.avgFillPrice; entryDate = (_k = (_h = portfoliosManager.getEntryDate(contractId)) !== null && _h !== void 0 ? _h : (_j = portfoliosManager.getLatestClosedPosition(contractId)) === null || _j === void 0 ? void 0 : _j.entryDate) !== null && _k !== void 0 ? _k : new Date(); trade = { id: "".concat(order.order.orderId), instrument: order.contract, entryPrice: entryPrice, entryDate: entryDate, type: order.order.orderType, price: order.orderStatus.avgFillPrice, quantity: order.order.totalQuantity, action: order.order.action, date: new Date() }; if (!this.completedTrades.has(permId)) { this.completedTrades.set(permId, trade); ibkrEvents.emit(events_1.IBKREVENTS.IBKR_SAVE_TRADE, trade); } this.openOrders.delete(permId); break; case ib_1.OrderStatus.ApiCancelled: case ib_1.OrderStatus.Cancelled: this.openOrders.delete(permId); this.cancelledOrders.set(permId, order); break; case ib_1.OrderStatus.Inactive: case ib_1.OrderStatus.PendingCancel: case ib_1.OrderStatus.PendingSubmit: case ib_1.OrderStatus.ApiPending: case ib_1.OrderStatus.Unknown: case ib_1.OrderStatus.PreSubmitted: case ib_1.OrderStatus.Submitted: this.openOrders.set(permId, order); break; default: break; } } return [2 /*return*/]; }); }); })]; case 2: _a.sent(); return [2 /*return*/]; } }); }); }; /** * getOpenOrders */ this.asyncOpenOrders = function () { return __awaiter(_this, void 0, void 0, function () { var openOrders, orders; var _this = this; return __generator(this, function (_a) { switch (_a.label) { case 0: if ((0, IBKRConnection_1.isMarketDataOnly)()) { (0, log_1.log)("Orders.asyncOpenOrders", "MD_ONLY enabled, skipping open order snapshot"); return [2 /*return*/, []]; } return [4 /*yield*/, (0, rxjs_1.firstValueFrom)(this.ib.getOpenOrders())]; case 1: openOrders = _a.sent(); orders = (0, compact_1.default)(openOrders.all.map(function (order) { if (!order) { return; } _this.openOrderQueue.push(order); return order; })); return [4 /*yield*/, this.processOrderQueue()]; case 2: _a.sent(); return [2 /*return*/, orders]; } }); }); }; this.syncOpenOrders = function () { if ((0, IBKRConnection_1.isMarketDataOnly)()) { (0, log_1.log)("Orders.syncOpenOrders", "MD_ONLY enabled, skipping open order subscription"); return; } var portfoliosManager = Portfolios_1.default.Instance; portfoliosManager.init(); _this.GetOrders = _this.ib.getAutoOpenOrders(true) .pipe((0, rxjs_1.catchError)(function (error) { (0, log_1.warn)("syncOpenOrders", "Error subscribing to open orders", error); return (0, rxjs_1.of)(null); })) .subscribe(function (openOrders) { openOrders.all.forEach(function (order) { if (!order) { return; } _this.openOrderQueue.push(order); }); _this.processOrderQueue(); }); }; /** * init */ this.init = function () { return __awaiter(_this, void 0, void 0, function () { var self, ib; return __generator(this, function (_a) { if ((0, IBKRConnection_1.isMarketDataOnly)()) { (0, log_1.log)("Orders.init", "MD_ONLY enabled, skipping orders init"); return [2 /*return*/]; } self = this; if (!self.ib) { ib = IBKRConnection_1.default.Instance.ib; self.ib = ib; this.syncOpenOrders(); } return [2 /*return*/]; }); }); }; this.parseOrder = function (orderPlaced, contractDetails) { if (contractDetails === null || contractDetails === void 0 ? void 0 : contractDetails.minTick) { if ((orderPlaced === null || orderPlaced === void 0 ? void 0 : orderPlaced.orderType) === ib_1.OrderType.STP_LMT || (orderPlaced === null || orderPlaced === void 0 ? void 0 : orderPlaced.orderType) === ib_1.OrderType.LMT) { if (orderPlaced === null || orderPlaced === void 0 ? void 0 : orderPlaced.lmtPrice) { orderPlaced.lmtPrice = Number((Math.round(orderPlaced.lmtPrice / contractDetails.minTick) * contractDetails.minTick).toFixed(2)); } } else { delete orderPlaced.lmtPrice; } if (orderPlaced === null || orderPlaced === void 0 ? void 0 : orderPlaced.auxPrice) { orderPlaced.auxPrice = Number((Math.round(orderPlaced.auxPrice / contractDetails.minTick) * contractDetails.minTick).toFixed(2)); } } ; var contract = (0, pickBy_1.default)((0, omit_1.default)(contractDetails.contract, ["primaryExch"]), identity_1.default); var order = (0, pickBy_1.default)(orderPlaced, identity_1.default); return { order: order, contract: contract }; }; /** * placeOrder */ this.placeOrder = function (contractDetails, orderToPlace) { return __awaiter(_this, void 0, void 0, function () { var _a, order, contract, _b, orderPlaced, error; return __generator(this, function (_c) { switch (_c.label) { case 0: _a = this.parseOrder(orderToPlace, contractDetails), order = _a.order, contract = _a.contract; (0, log_utils_1.logOrder)("Orders.placeOrder Placing order ".concat(order.orderId || ""), { order: order, contract: contract }); return [4 /*yield*/, (0, awaitP_1.default)(this.ib.placeNewOrder(contractDetails, order))]; case 1: _b = _c.sent(), orderPlaced = _b[0], error = _b[1]; if (error) { (0, log_utils_1.logOrder)("Orders.placeOrder Error placing order ".concat(error), { order: order, contract: contract }, true); return [2 /*return*/]; } if (orderPlaced) { // TODO save order tick, entry (0, log_utils_1.logOrder)("Orders.placeOrder Order placed id=".concat(orderPlaced || order.orderId), { order: order, contract: contract }); return [2 /*return*/, true]; } (0, log_utils_1.logOrder)("Orders.placeOrder Order NOT placed ".concat(order.orderId || ""), { order: order, contract: contract }, true); return [2 /*return*/, false]; } }); }); }; /** * modifyOrder */ this.modifyOrder = function (id, contractDetails, orderToPlace) { return __awaiter(_this, void 0, void 0, function () { var _a, order, contract; return __generator(this, function (_b) { try { _a = this.parseOrder(orderToPlace, contractDetails), order = _a.order, contract = _a.contract; this.ib.modifyOrder(id, contractDetails, order); // TODO save order tick, entry (0, log_utils_1.logOrder)("Orders.modifyOrder ".concat(id), { order: order, contract: contract }); return [2 /*return*/, true]; } catch (e) { (0, log_1.log)("Orders.modifyOrder", "Error modifying order ".concat(id)); return [2 /*return*/, false]; } return [2 /*return*/]; }); }); }; this.cancelOrder = function (orderId, orderCancel) { return __awaiter(_this, void 0, void 0, function () { return __generator(this, function (_a) { try { this.ib.cancelOrder(orderId, orderCancel); // TODO save order tick, entry (0, log_1.log)("Orders.cancelOrder", "Order cancelled ".concat(orderId)); return [2 /*return*/, true]; } catch (e) { (0, log_1.log)("Orders.cancelOrder", "Error modifying order ".concat(orderId)); return [2 /*return*/, false]; } return [2 /*return*/]; }); }); }; this.cancelAllOrders = function (orderCancel) { return __awaiter(_this, void 0, void 0, function () { return __generator(this, function (_a) { try { this.ib.cancelAllOrders(orderCancel); // TODO save order tick, entry (0, log_1.log)("Orders.cancelAllOrders", "Order placed ".concat(orderCancel)); return [2 /*return*/, true]; } catch (e) { (0, log_1.log)("Orders.cancelAllOrders", "Error modifying order ".concat(orderCancel)); return [2 /*return*/, false]; } return [2 /*return*/]; }); }); }; } Object.defineProperty(Orders, "Instance", { get: function () { return this._instance || (this._instance = new this()); }, enumerable: false, configurable: true }); Object.defineProperty(Orders.prototype, "orders", { get: function () { return Array.from(this.openOrders.values()); }, enumerable: false, configurable: true }); Object.defineProperty(Orders.prototype, "trades", { get: function () { return Array.from(this.completedTrades.values()); }, enumerable: false, configurable: true }); return Orders; }()); exports.Orders = Orders; exports.default = Orders; //# sourceMappingURL=Orders.js.map