@stoqey/ibkr
Version:
NodeJS Interactive Brokers wrapper & utilities using @stoqey/ib
395 lines • 22.4 kB
JavaScript
"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