UNPKG

@stoqey/ibkr

Version:

NodeJS Interactive Brokers wrapper & utilities using @stoqey/ib

420 lines 26 kB
"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 __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 __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { if (ar || !(i in from)) { if (!ar) ar = Array.prototype.slice.call(from, 0, i); ar[i] = from[i]; } } return to.concat(ar || Array.prototype.slice.call(from)); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.MarketDataManager = void 0; var moment_1 = __importDefault(require("moment")); var rxjs_1 = require("rxjs"); var IBKRConnection_1 = __importDefault(require("../connection/IBKRConnection")); var awaitP_1 = __importDefault(require("../utils/awaitP")); var log_1 = require("../utils/log"); var events_1 = require("../events"); var Portfolios_1 = __importDefault(require("../portfolios/Portfolios")); var isEmpty_1 = __importDefault(require("lodash/isEmpty")); var time_utils_1 = require("../utils/time.utils"); var instrument_utils_1 = require("../utils/instrument.utils"); var data_utils_1 = require("../utils/data.utils"); var chart_1 = require("../utils/chart"); var sortBy_1 = __importDefault(require("lodash/sortBy")); var appEvents = events_1.IBKREvents.Instance; var logsNames = 'MkdMgr'; var MarketDataManager = /** @class */ (function () { function MarketDataManager() { var _this = this; this.marketData = {}; this.GetHistoricalDataUpdates = new Map(); this.getSymbolKey = function (contract) { return (0, instrument_utils_1.getSymbolKey)(contract); }; // TODO api data this.historicalData = function (contract, startDate, endDate, interval) { return __awaiter(_this, void 0, void 0, function () { var symbol, instrumentData, dataArray, filteredData, first, last; return __generator(this, function (_a) { // throw new Error("Method not implemented."); // i have the data as this.marketData: Record<string, { [date: string]: MarketData }> = {}; // convert to array using key as the date, then filter by start and end date if (!contract) { (0, log_1.log)("instrument not found", contract); return [2 /*return*/, []]; } symbol = this.getSymbolKey(contract); instrumentData = this.marketData[symbol]; if (!instrumentData) { (0, log_1.log)("instrument data not found", symbol); return [2 /*return*/, []]; } dataArray = Object.keys(instrumentData).filter(function (date) { var dataDate = new Date(date); return dataDate >= new Date(startDate) && dataDate <= new Date(endDate); }).map(function (date) { return instrumentData[date]; }); filteredData = dataArray.filter(function (data) { var dataDate = new Date(data.date); return dataDate >= new Date(startDate) && dataDate <= new Date(endDate); }); first = filteredData && filteredData[0]; last = (filteredData || [])[(filteredData === null || filteredData === void 0 ? void 0 : filteredData.length) - 1]; (0, chart_1.plotMkdCli)(filteredData); (0, log_1.log)("".concat(logsNames, ".historicalData"), "".concat(filteredData.length, " data points for ").concat(symbol, " from ").concat((0, time_utils_1.formatDateStr)(first.date), " to ").concat((0, time_utils_1.formatDateStr)(last.date), " @").concat((0, data_utils_1.formatDec)(first.close), " -> @").concat((0, data_utils_1.formatDec)(last.close))); return [2 /*return*/, filteredData]; }); }); }; // TODO api data this.getQuote = function (contract, date) { return __awaiter(_this, void 0, void 0, function () { var symbol; return __generator(this, function (_a) { try { symbol = this.getSymbolKey(contract); return [2 /*return*/, this.marketData[symbol] ? this.marketData[symbol][date.toISOString()] : null]; } catch (e) { (0, log_1.log)("error getting quote ".concat(JSON.stringify(contract || {})), e); return [2 /*return*/, null]; } return [2 /*return*/]; }); }); }; this.removeHistoricalDataUpdates = function (contract) { var symbolId = _this.getSymbolKey(contract); var subscription = _this.GetHistoricalDataUpdates.get(symbolId); if (subscription) { subscription.unsubscribe(); _this.GetHistoricalDataUpdates.delete(symbolId); } }; this.getHistoricalDataUpdates = function (contract, barSizeSetting, whatToShow) { return __awaiter(_this, void 0, void 0, function () { var portfoliosManager_1, symbolId_1, getMarketDataLast30Minutes, e_1; var _this = this; return __generator(this, function (_a) { switch (_a.label) { case 0: _a.trys.push([0, 4, , 5]); portfoliosManager_1 = Portfolios_1.default.Instance; if (!(!contract.conId || !contract.exchange)) return [3 /*break*/, 2]; return [4 /*yield*/, this.getContract(contract)]; case 1: contract = _a.sent(); _a.label = 2; case 2: symbolId_1 = this.getSymbolKey(contract); if (this.GetHistoricalDataUpdates.has(symbolId_1)) { (0, log_1.warn)("".concat(logsNames, ".getHistoricalDataUpdates"), "Already subscribed to ".concat(symbolId_1)); return [2 /*return*/]; } getMarketDataLast30Minutes = function () { return __awaiter(_this, void 0, void 0, function () { var endDateTime, durationStr, barSizeSetting5Sec, historicalData, first, last, lastMarketData; var _this = this; return __generator(this, function (_a) { switch (_a.label) { case 0: (0, log_1.log)("".concat(logsNames, ".getHistoricalDataUpdates.getMarketDataLast30Minutes"), "".concat(symbolId_1)); endDateTime = (0, moment_1.default)().format('YYYYMMDD HH:mm:ss'); durationStr = '3600 S'; barSizeSetting5Sec = '5 secs'; return [4 /*yield*/, (0, awaitP_1.default)(this.getHistoricalData(contract, endDateTime, durationStr, barSizeSetting5Sec, whatToShow))]; case 1: historicalData = (_a.sent())[0]; // set market data if (!(0, isEmpty_1.default)(historicalData)) { first = historicalData && historicalData[0]; last = (historicalData || [])[historicalData.length - 1]; (0, chart_1.plotMkdCli)(historicalData); (0, log_1.log)("".concat(logsNames, ".getHistoricalDataUpdates.getMarketDataLast30Minutes.length"), "".concat(historicalData === null || historicalData === void 0 ? void 0 : historicalData.length, " data points for ").concat(symbolId_1, " from ").concat((0, time_utils_1.formatDateStr)(first === null || first === void 0 ? void 0 : first.date), " to ").concat((0, time_utils_1.formatDateStr)(last === null || last === void 0 ? void 0 : last.date), " @").concat((0, data_utils_1.formatDec)(first === null || first === void 0 ? void 0 : first.close), " -> @").concat((0, data_utils_1.formatDec)(last === null || last === void 0 ? void 0 : last.close), " whatToShow=").concat(whatToShow)); if (!this.marketData[symbolId_1]) { this.marketData[symbolId_1] = {}; } historicalData.forEach(function (marketDataItem) { var _a; var dateIso = marketDataItem.date.toISOString(); _this.marketData[symbolId_1] = __assign(__assign({}, _this.marketData[symbolId_1]), (_a = {}, _a[dateIso] = marketDataItem, _a)); }); lastMarketData = historicalData[historicalData.length - 1]; if (lastMarketData) { portfoliosManager_1.updateMarketPrice(contract.conId, lastMarketData.close); } } return [2 /*return*/]; } }); }); }; return [4 /*yield*/, getMarketDataLast30Minutes()]; case 3: _a.sent(); (0, log_1.log)("".concat(logsNames, ".getHistoricalDataUpdates"), "Subscribing to ".concat(symbolId_1)); this.GetHistoricalDataUpdates.set(symbolId_1, IBKRConnection_1.default.Instance.ib.getHistoricalDataUpdates(contract, barSizeSetting, whatToShow, 2) // .pipe( // catchError((error) => { // log(`${logsNames}.HDU`, `Error fetching historical data for ${symbolId}: ${error.message}`); // return of(); // Return an empty observable to complete the stream // }) // ) .subscribe(function (bar) { var _a; var date = new Date(+bar.time * 1000); var dateIso = date.toISOString(); var marketDataItem = { instrument: contract, date: date, open: bar.open, high: bar.high, low: bar.low, close: bar.close, volume: bar.volume, wap: bar.WAP, vwap: bar.WAP, // same as wap count: bar === null || bar === void 0 ? void 0 : bar.count, }; if (!_this.marketData[symbolId_1]) { _this.marketData[symbolId_1] = {}; } _this.marketData[symbolId_1] = __assign(__assign({}, _this.marketData[symbolId_1]), (_a = {}, _a[dateIso] = marketDataItem, _a)); if (bar === null || bar === void 0 ? void 0 : bar.close) { portfoliosManager_1.updateMarketPrice(contract.conId, bar.close); } appEvents.emit(events_1.IBKREVENTS.IBKR_BAR, marketDataItem); (0, log_1.log)("".concat(logsNames, ".HDU"), "bar for ".concat(symbolId_1, " at ").concat((0, time_utils_1.formatDateStr)(new Date(dateIso)), " @").concat(bar.close, " ").concat(bar.volume ? "vol=".concat(bar.volume) : '')); })); return [3 /*break*/, 5]; case 4: e_1 = _a.sent(); (0, log_1.warn)("getHistoricalDataUpdates error", e_1); return [2 /*return*/]; case 5: return [2 /*return*/]; } }); }); }; this.getHistoricalData = function (contract_1, endDateTime_1, durationStr_1, barSizeSetting_1, whatToShow_1) { var args_1 = []; for (var _i = 5; _i < arguments.length; _i++) { args_1[_i - 5] = arguments[_i]; } return __awaiter(_this, __spreadArray([contract_1, endDateTime_1, durationStr_1, barSizeSetting_1, whatToShow_1], args_1, true), void 0, function (contract, endDateTime, durationStr, barSizeSetting, whatToShow, useRTH) { var _a, contractInstrument, errContract, symbol, _b, bars, err, mkd; if (useRTH === void 0) { useRTH = false; } return __generator(this, function (_c) { switch (_c.label) { case 0: return [4 /*yield*/, (0, awaitP_1.default)(this.getContract(contract))]; case 1: _a = _c.sent(), contractInstrument = _a[0], errContract = _a[1]; if (errContract) { (0, log_1.warn)("getHistoricalData contract err", errContract); return [2 /*return*/, null]; } if (!contractInstrument) { (0, log_1.warn)("getHistoricalData contract not found", contract); return [2 /*return*/, null]; } symbol = this.getSymbolKey(contractInstrument); return [4 /*yield*/, (0, awaitP_1.default)(IBKRConnection_1.default.Instance.ib.getHistoricalData(contractInstrument, endDateTime, durationStr, barSizeSetting, whatToShow, useRTH, 2))]; case 2: _b = _c.sent(), bars = _b[0], err = _b[1]; if (bars && bars.length > 0) { mkd = bars.map(function (bar) { var date = new Date(+bar.time * 1000); var marketDataItem = { instrument: contractInstrument === null || contractInstrument === void 0 ? void 0 : contractInstrument.contract, date: date, open: bar.open, high: bar.high, low: bar.low, close: bar.close, volume: bar.volume, wap: bar.WAP, vwap: bar.WAP, // same as wap count: bar === null || bar === void 0 ? void 0 : bar.count, }; return marketDataItem; }); return [2 /*return*/, (0, sortBy_1.default)(mkd, 'date')]; } if (err) { (0, log_1.warn)("getHistoricalData err ".concat(symbol), err); } return [2 /*return*/, null]; } }); }); }; this.getHistoricalTicksLast = function (contract_1, startDate_1, endDate_1) { var args_1 = []; for (var _i = 3; _i < arguments.length; _i++) { args_1[_i - 3] = arguments[_i]; } return __awaiter(_this, __spreadArray([contract_1, startDate_1, endDate_1], args_1, true), void 0, function (contract, startDate, endDate, numberOfTicks, useRTH) { var startDateTime, endDateTime, _a, contractInstrument, errContract, symbol, _b, ticks, err, mkd; if (numberOfTicks === void 0) { numberOfTicks = 1000; } if (useRTH === void 0) { useRTH = false; } return __generator(this, function (_c) { switch (_c.label) { case 0: if (!startDate) { (0, log_1.warn)("getHistoricalTicksLast startDate not set"); return [2 /*return*/, null]; } if (!endDate) { (0, log_1.warn)("getHistoricalTicksLast endDate not set"); return [2 /*return*/, null]; } if (startDate > endDate) { (0, log_1.warn)("getHistoricalTicksLast startDate cannot be great than endDate"); return [2 /*return*/, null]; } startDateTime = (0, moment_1.default)(startDate).format('YYYYMMDD HH:mm:ss'); endDateTime = (0, moment_1.default)(endDate).format('YYYYMMDD HH:mm:ss'); return [4 /*yield*/, (0, awaitP_1.default)(this.getContract(contract))]; case 1: _a = _c.sent(), contractInstrument = _a[0], errContract = _a[1]; if (errContract) { (0, log_1.warn)("getHistoricalTicksLast contract err", errContract); return [2 /*return*/, null]; } if (!contractInstrument) { (0, log_1.warn)("getHistoricalTicksLast contract not found", contract); return [2 /*return*/, null]; } symbol = this.getSymbolKey(contractInstrument); return [4 /*yield*/, (0, awaitP_1.default)((0, rxjs_1.lastValueFrom)(IBKRConnection_1.default.Instance.ib.getHistoricalTicksLast(contractInstrument, startDateTime, endDateTime, numberOfTicks, useRTH)))]; case 2: _b = _c.sent(), ticks = _b[0], err = _b[1]; if (ticks && ticks.length > 0) { mkd = ticks.map(function (bar) { var date = new Date(+bar.time * 1000); var tickDataItem = { contract: contractInstrument === null || contractInstrument === void 0 ? void 0 : contractInstrument.contract, date: date, price: bar.price, size: bar.size, exchange: bar.exchange, specialConditions: bar.specialConditions, }; return tickDataItem; }); return [2 /*return*/, (0, sortBy_1.default)(mkd, 'date')]; } if (err) { (0, log_1.warn)("getHistoricalTicksLast err ".concat(symbol), err); } return [2 /*return*/, null]; } }); }); }; this.getContract = function (contract) { return __awaiter(_this, void 0, void 0, function () { var _a, contracts, err, firstContract; return __generator(this, function (_b) { switch (_b.label) { case 0: if (contract === null || contract === void 0 ? void 0 : contract.contract) { return [2 /*return*/, contract]; } ; return [4 /*yield*/, (0, awaitP_1.default)(this.ib.getContractDetails(contract))]; case 1: _a = _b.sent(), contracts = _a[0], err = _a[1]; if (contracts && contracts.length > 0) { firstContract = contracts[0]; return [2 /*return*/, __assign(__assign({}, firstContract), firstContract.contract)]; } if (err) { (0, log_1.warn)("getContract err ".concat(JSON.stringify(contract || {})), err); } return [2 /*return*/, null]; } }); }); }; this.searchContracts = function (contract) { return __awaiter(_this, void 0, void 0, function () { var _a, contracts, err; return __generator(this, function (_b) { switch (_b.label) { case 0: return [4 /*yield*/, (0, awaitP_1.default)(this.ib.getContractDetails(contract))]; case 1: _a = _b.sent(), contracts = _a[0], err = _a[1]; if (contracts && contracts.length > 0) { return [2 /*return*/, contracts.map(function (c) { return __assign(__assign({}, c), c.contract); })]; } if (err) { (0, log_1.warn)("getContract err ".concat(JSON.stringify(contract || {})), err); } return [2 /*return*/, []]; } }); }); }; this.init = function () { var ib = IBKRConnection_1.default.Instance.ib; if (!_this.ib) { _this.ib = ib; // ib.getHistoricalData // async // ib.getHistoricalDataUpdates() // subscribe } }; } Object.defineProperty(MarketDataManager, "Instance", { get: function () { return this._instance || (this._instance = new this()); }, enumerable: false, configurable: true }); return MarketDataManager; }()); exports.MarketDataManager = MarketDataManager; exports.default = MarketDataManager; //# sourceMappingURL=MarketDataManager.js.map