UNPKG

sec-edgar-api

Version:

Fetch and parse SEC earnings reports and other filings. Useful for financial analysis.

247 lines (246 loc) 15.4 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 __rest = (this && this.__rest) || function (s, e) { var t = {}; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p]; if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]]; } return t; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.parseXbrl = void 0; var constants_1 = require("../../../util/constants"); var util_xbrl_1 = require("../../../util/util-xbrl"); var FactFiscalCalculator_1 = require("../../ReportRawBuilder/FactFiscalCalculator"); var FactPeriodResolver_1 = require("../../ReportRawBuilder/FactPeriodResolver"); var XBRLParser_1 = require("../XBRLParser/XBRLParser"); function isWithinDays(params) { var dateA = params.dateA, dateB = params.dateB, days = params.days; if (dateA === dateB) return true; var timeDiff = Math.abs(new Date(dateA).getTime() - new Date(dateB).getTime()); var daysDiff = timeDiff / (1000 * 3600 * 24); return daysDiff < days; } function buildReportsFromFacts(params) { var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x; var filing = params.filing, facts = params.facts, fiscalPeriod = params.fiscalPeriod, fiscalYear = params.fiscalYear, pathSeparator = params.pathSeparator, cikProp = params.cik; var urlParts = (_a = filing === null || filing === void 0 ? void 0 : filing.url.split('/')) !== null && _a !== void 0 ? _a : []; var cik = cikProp !== null && cikProp !== void 0 ? cikProp : urlParts[(_b = urlParts.indexOf('data')) !== null && _b !== void 0 ? _b : -1]; var reportFactValues = { cik: Number(cik), dateReport: (_c = filing === null || filing === void 0 ? void 0 : filing.reportDate) !== null && _c !== void 0 ? _c : '', fiscalPeriod: fiscalPeriod, fiscalYear: fiscalYear, }; // if there is a split fact, make sure it's from the current fiscal year var splitFact = facts.find(function (f) { var _a; return f.name.endsWith(constants_1.KEY_SPLIT) && isWithinDays({ dateA: f.filed, dateB: (_a = filing === null || filing === void 0 ? void 0 : filing.reportDate) !== null && _a !== void 0 ? _a : '', days: 90 }); }); var reportFocus = { cik: Number(cik !== null && cik !== void 0 ? cik : reportFactValues.cik), dateFiled: (_d = filing === null || filing === void 0 ? void 0 : filing.filingDate) !== null && _d !== void 0 ? _d : '', dateReport: String((_e = filing === null || filing === void 0 ? void 0 : filing.reportDate) !== null && _e !== void 0 ? _e : reportFactValues.dateReport), fiscalPeriod: (fiscalPeriod !== null && fiscalPeriod !== void 0 ? fiscalPeriod : reportFactValues.fiscalPeriod), fiscalYear: Number(fiscalYear !== null && fiscalYear !== void 0 ? fiscalYear : reportFactValues.fiscalYear), splitDate: (_f = splitFact === null || splitFact === void 0 ? void 0 : splitFact.end) !== null && _f !== void 0 ? _f : null, splitRatio: (splitFact === null || splitFact === void 0 ? void 0 : splitFact.value) ? Number(splitFact.value) : null, url: (_g = filing === null || filing === void 0 ? void 0 : filing.url) !== null && _g !== void 0 ? _g : '', }; var reportByDateRange = {}; var factByName = new Map(); var roundPlacesByName = new Map(); var scaleByName = new Map(); var isFocusFactByDateKey = new Map(); var fiscalsByDateKey = new Map(); var focusPeriodByQuarter = { 1: 3, 2: 6, 3: 9, 4: 12, }; for (var _i = 0, facts_1 = facts; _i < facts_1.length; _i++) { var fact = facts_1[_i]; var period = FactPeriodResolver_1.default.getPeriod({ start: fact.start, end: fact.end }); var dateKey = fact.start ? "".concat(fact.start, "_").concat(fact.end, "_").concat(period) : "".concat(fact.end, "_").concat(period); if (!fiscalsByDateKey.has(dateKey)) { var fiscalPeriod_1 = (period === 12 && fact.quarter === 4 ? 'FY' : "Q".concat(fact.quarter)); fiscalsByDateKey.set(dateKey, { fiscalYear: fact.fiscalYear, fiscalPeriod: fiscalPeriod_1 }); } var isSplitFact = fact === splitFact; var focusPeriod = (_h = focusPeriodByQuarter[fact.quarter]) !== null && _h !== void 0 ? _h : 0; var isFocusPeriod = period === focusPeriod || period === 0; var isFocusFact = (_j = isFocusFactByDateKey.get(dateKey)) !== null && _j !== void 0 ? _j : (isFocusPeriod && (isWithinDays({ dateA: fact.end, dateB: reportFocus.dateReport, days: 45 }) || isSplitFact)); (_k = reportByDateRange[dateKey]) !== null && _k !== void 0 ? _k : (reportByDateRange[dateKey] = { cik: reportFocus.cik, url: (_l = filing === null || filing === void 0 ? void 0 : filing.url) !== null && _l !== void 0 ? _l : '', splitDate: (_m = splitFact === null || splitFact === void 0 ? void 0 : splitFact.end) !== null && _m !== void 0 ? _m : null, splitRatio: (splitFact === null || splitFact === void 0 ? void 0 : splitFact.value) ? Number(splitFact.value) : null, dateFiled: reportFocus.dateFiled, dateReport: fact.end, fiscalPeriod: (_o = fiscalsByDateKey.get(dateKey)) === null || _o === void 0 ? void 0 : _o.fiscalPeriod, startDate: (_p = fact.start) !== null && _p !== void 0 ? _p : '', endDate: fact.end, fiscalYear: (_q = fiscalsByDateKey.get(dateKey)) === null || _q === void 0 ? void 0 : _q.fiscalYear, period: period, isCurrentPeriod: isFocusFact, }); if (isFocusFact) { fact.isCurrentPeriod = true; } if (!isSplitFact) { isFocusFactByDateKey.set(dateKey, isFocusFact); } var el = fact; var scale = Number((_r = el.scale) !== null && _r !== void 0 ? _r : 0) || 0; var decimals = Number((_s = el.decimals) !== null && _s !== void 0 ? _s : 0) || 0; var suffix = fact.name.includes(pathSeparator) ? null : (_t = fact === null || fact === void 0 ? void 0 : fact.segments) === null || _t === void 0 ? void 0 : _t.map(function (_a) { var dimension = _a.dimension, value = _a.value; return "".concat(dimension).concat(pathSeparator).concat(value); }).join(pathSeparator); var nameKey = suffix ? "".concat(fact.name).concat(pathSeparator).concat(suffix) : fact.name; var roundPlaces = scale + decimals; var prevFactKey = "".concat(nameKey, "-").concat(dateKey); var prevFact = factByName.get(prevFactKey); var hasValue = Boolean(fact.value); var hadValue = Boolean(prevFact === null || prevFact === void 0 ? void 0 : prevFact.value); if (hasValue && hadValue && fact.value !== (prevFact === null || prevFact === void 0 ? void 0 : prevFact.value)) { var prevRounding = roundPlacesByName.get(prevFactKey); var prevScale = scaleByName.get(prevFactKey); var prevFact_1 = factByName.get(prevFactKey); var prevUnit = (_v = (_u = prevFact_1 === null || prevFact_1 === void 0 ? void 0 : prevFact_1.unit) === null || _u === void 0 ? void 0 : _u.split('_').pop()) === null || _v === void 0 ? void 0 : _v.toLowerCase(); var unit = (_x = (_w = fact.unit) === null || _w === void 0 ? void 0 : _w.split('_').pop()) === null || _x === void 0 ? void 0 : _x.toLowerCase(); var shouldSkip = [ (prevUnit === null || prevUnit === void 0 ? void 0 : prevUnit.length) === 3 && (unit === null || unit === void 0 ? void 0 : unit.length) === 3 && prevUnit !== unit && prevUnit === 'usd', (prevRounding !== null && prevRounding !== void 0 ? prevRounding : 0) < roundPlaces, (prevScale !== null && prevScale !== void 0 ? prevScale : 0) < scale, (prevRounding !== null && prevRounding !== void 0 ? prevRounding : 0) === roundPlaces && (prevScale !== null && prevScale !== void 0 ? prevScale : 0) === scale && prevUnit === unit, ].some(Boolean); if (shouldSkip) continue; } roundPlacesByName.set(prevFactKey, roundPlaces); scaleByName.set(prevFactKey, scale); factByName.set(prevFactKey, fact); reportByDateRange[dateKey][nameKey] = fact.value; if (isFocusFact) { if (prevFact) prevFact.isUsedInReport = false; fact.isUsedInReport = true; reportFocus[nameKey] = fact.value; } else { fact.isUsedInReport = false; } } return { reportFocus: reportFocus, reportByDateRange: reportByDateRange, factsFiltered: Array.from(factByName.values()) }; } function parseXbrl(params) { var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q; var parser = new XBRLParser_1.default(); var xml = params.xml, _r = params.includeReport, includeReport = _r === void 0 ? true : _r, options = __rest(params, ["xml", "includeReport"]); var response = parser.parse(xml, options); var _s = (_b = (_a = response.instance) === null || _a === void 0 ? void 0 : _a.xbrl) !== null && _b !== void 0 ? _b : {}, _t = _s.contexts, contexts = _t === void 0 ? [] : _t, _u = _s.factElements, factElements = _u === void 0 ? [] : _u; var labelByTaxonomy = (0, util_xbrl_1.getLabelByTaxonomy)((_d = (_c = response.linkbaseLabel) === null || _c === void 0 ? void 0 : _c.xbrl) !== null && _d !== void 0 ? _d : {}); var contextById = new Map(); contexts.forEach(function (context) { return contextById.set(context.id, context); }); var cik = response.header.cik; var accessionNumber = response.header.accessionNumber; var accessionNumberNoHyphens = accessionNumber.replace(/-/g, ''); var fiscalCalculator = new FactFiscalCalculator_1.default({ fiscalYearEnd: { day: Number(response.header.fiscalYearEnd.substring(2)), month: Number(response.header.fiscalYearEnd.substring(0, 2)), }, }); var facts = []; for (var _i = 0, factElements_1 = factElements; _i < factElements_1.length; _i++) { var fact = factElements_1[_i]; var context = contextById.get(fact.contextRef); var end = (_f = (_e = context === null || context === void 0 ? void 0 : context.period.endDate) !== null && _e !== void 0 ? _e : context === null || context === void 0 ? void 0 : context.period.instant) !== null && _f !== void 0 ? _f : ''; var start = context === null || context === void 0 ? void 0 : context.period.startDate; var _v = fiscalCalculator.getFiscalYearQuarter({ dateStr: end }), quarter = _v.quarter, year = _v.year; var factParsed = { cik: cik, end: end, filed: response.header.filingDate, name: fact.name, unit: (_g = fact.unitRef) !== null && _g !== void 0 ? _g : 'pure', value: isNaN(Number(fact.text)) ? String(fact.text) : Number(fact.text), accn: accessionNumber, form: response.header.form, segments: (_h = context === null || context === void 0 ? void 0 : context.entity.segment) !== null && _h !== void 0 ? _h : [], start: start, contextRef: fact.contextRef, label: (_j = labelByTaxonomy[fact.name]) !== null && _j !== void 0 ? _j : fact.name, period: FactPeriodResolver_1.default.getPeriod({ start: start, end: end }), quarter: quarter, fiscalYear: year, }; if (factParsed.decimals) { factParsed.decimals = Number(fact.decimals); } if (fact.scale) { factParsed.scale = Number(fact.scale); } facts.push(factParsed); } var fiscalPeriodFact = facts.find(function (f) { return f.name === 'dei:DocumentFiscalPeriodFocus'; }); var fiscalYearFact = facts.find(function (f) { return f.name === 'dei:DocumentFiscalYearFocus'; }); var fiscalYear = fiscalYearFact ? Number(fiscalYearFact.value) : 0; var fiscalPeriod = fiscalPeriodFact ? fiscalPeriodFact.value : 'FY'; if (!fiscalPeriodFact || !fiscalYearFact) { var reportDate = (_k = response.header.reportDate) !== null && _k !== void 0 ? _k : (_m = (_l = response.instance) === null || _l === void 0 ? void 0 : _l.xbrl.factElements.find(function (f) { return f.name === 'dei:DocumentPeriodEndDate'; })) === null || _m === void 0 ? void 0 : _m.text; if (!reportDate) { throw new Error("Report date not found. Unable to determine fiscal year and period. accn: ".concat(accessionNumber)); } var _w = fiscalCalculator.getFiscalYearQuarter({ dateStr: reportDate !== null && reportDate !== void 0 ? reportDate : '' }), quarter = _w.quarter, year = _w.year; fiscalYear = year; fiscalPeriod = (quarter === 4 ? 'FY' : "Q".concat(quarter)); } var factsForBuilder = includeReport ? facts : []; var _x = buildReportsFromFacts({ facts: factsForBuilder, pathSeparator: '>', fiscalPeriod: (_o = factsForBuilder.find(function (f) { return f.name === 'dei:DocumentFiscalPeriodFocus'; })) === null || _o === void 0 ? void 0 : _o.value, fiscalYear: Number((_q = (_p = factsForBuilder.find(function (f) { return f.name === 'dei:DocumentFiscalYearFocus'; })) === null || _p === void 0 ? void 0 : _p.value) !== null && _q !== void 0 ? _q : 0), cik: response.header.cik, filing: { acceptanceDateTime: response.header.acceptanceDatetime, accessionNumber: accessionNumber, act: response.header.act, fileNumber: response.header.fileNumber, filingDate: response.header.filingDate, form: response.header.form, filmNumber: response.header.filmNumber, isInlineXBRL: 1, isXBRL: 1, items: '', primaryDocDescription: '', primaryDocument: '', reportDate: response.header.reportDate, size: 1, url: "https://www.sec.gov/Archives/edgar/data/".concat(cik, "/").concat(accessionNumberNoHyphens, "/").concat(accessionNumber, ".txt"), urlPrimaryDocument: '', }, }), factsFiltered = _x.factsFiltered, reportFocus = _x.reportFocus, reportByDateRange = _x.reportByDateRange; // Some concepts have members, but do not have a sum. add the sum to the report. var periodReports = Object.values(reportByDateRange); return __assign(__assign({}, response), { fiscalYear: fiscalYear, fiscalPeriod: fiscalPeriod, facts: factsFiltered, report: factsFiltered.length > 0 ? reportFocus : null, xml: xml, periodReports: periodReports }); } exports.parseXbrl = parseXbrl;