UNPKG

sec-edgar-api

Version:

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

242 lines (241 loc) 13.5 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); /** * Resolves quarterly and annual values for a property. This is because filers provide total values for the * current fiscal year, rather than values for the individual quarters * ex: Net income for Q2 will give 6 months revenue, not 3 month. */ var FactPeriodResolver = /** @class */ (function () { function FactPeriodResolver(args) { /** Values for each quarter [numQ1, numQ2, numQ3, numQ4] */ this.valueByQuarterByPropertyByYear = new Map(); /** Same as by quarter but string values */ this.valueByQuarterByPropertyByYearString = new Map(); /** Value sums (each includes sum of previous quarter). last el used for annual report. [sumQ1, sumQ2, sumQ3, sumFY] */ this.sumByQuarterByPropertyByYear = new Map(); /** Which properties have a range that need to be resolved to get quarterly value */ this.propertiesResolvableByYear = new Map(); /** all properties present for each year */ this.propertiesByYear = new Map(); /** prevent values from being added multiple times */ this.resolvedValues = new Set(); this.unresolvedReports = new Map(); var cik = args.cik, _a = args.preferOriginalFilingValues, preferOriginalFilingValues = _a === void 0 ? false : _a; this.cik = cik; this.preferOriginalFilingValues = preferOriginalFilingValues; } /** * Some properties have a start and end that represent a period average, rather than a period total. * These properties should be treated as instantaneous properties, meaning * the value for Q4 and FY should be the same. * * I believe the only properties like this are share related: * us-gaap:WeightedAverageNumberOfDilutedSharesOutstanding and us-gaap:WeightedAverageNumberOfSharesOutstandingBasic. * May need to update this in the future if there are more */ FactPeriodResolver.isAverageShares = function (params) { var propertyName = params.propertyName; return (propertyName.includes('Average') && (propertyName.includes('SharesOutstanding') || propertyName.includes('SharesIssued'))); }; FactPeriodResolver.prototype.isAverageShares = function (params) { return FactPeriodResolver.isAverageShares(params); }; /** * Used to check if this should potentially overwrite a value that has already been set. */ FactPeriodResolver.prototype.isPreferredValue = function (params) { var bucket = params.bucket, end = params.end, filed = params.filed, bucketIndex = params.bucketIndex; return !bucket.has(bucketIndex) || this.preferOriginalFilingValues || !this.isOriginalFiling({ end: end, filed: filed }); }; FactPeriodResolver.prototype.getOrSetBucketArr = function (map, year, propertyName) { var _a, _b; var propertyMap = (_a = map.get(year)) !== null && _a !== void 0 ? _a : map.set(year, new Map()).get(year); return (_b = propertyMap.get(propertyName)) !== null && _b !== void 0 ? _b : propertyMap.set(propertyName, new Map()).get(propertyName); }; FactPeriodResolver.prototype.addPropertyByYear = function (bucket, year, propertyName) { var _a; var properties = (_a = bucket.get(year)) !== null && _a !== void 0 ? _a : bucket.set(year, new Set()).get(year); properties.add(propertyName); }; FactPeriodResolver.prototype.resolveValues = function (propertyName, year) { var _a, _b, _c; var keyResolved = "".concat(year, "_").concat(propertyName); var isResolved = this.resolvedValues.has(keyResolved); var isResolvable = Boolean((_a = this.propertiesResolvableByYear.get(year)) === null || _a === void 0 ? void 0 : _a.has(propertyName)); if (!isResolvable || isResolved) return; var _d = this.getPropertyBuckets(year, propertyName), bucketQuarter = _d.bucketQuarter, bucketSum = _d.bucketSum; if (bucketQuarter.size === 4 && bucketSum.has(3)) return; for (var i = 0; i < 4; i++) { var quarterValue = bucketQuarter.get(i); var reportKey = "".concat(year, "_").concat(i + 1); var unresolvedReport = this.unresolvedReports.get(reportKey); var unresolvedReportPrev = this.unresolvedReports.get("".concat(year, "_").concat(i)); var sumValue = (_b = unresolvedReport === null || unresolvedReport === void 0 ? void 0 : unresolvedReport[propertyName]) !== null && _b !== void 0 ? _b : 0; var prevSum = (_c = unresolvedReportPrev === null || unresolvedReportPrev === void 0 ? void 0 : unresolvedReportPrev[propertyName]) !== null && _c !== void 0 ? _c : 0; if (quarterValue === undefined) { var bucketQuarter_1 = this.getOrSetBucketArr(this.valueByQuarterByPropertyByYear, year, propertyName); bucketQuarter_1.set(i, (Number(sumValue) || 0) - (Number(prevSum) || 0)); } if (quarterValue !== undefined && sumValue === undefined) { var bucketSum_1 = this.getOrSetBucketArr(this.sumByQuarterByPropertyByYear, year, propertyName); bucketSum_1.set(i, quarterValue + prevSum); } } this.resolvedValues.add(keyResolved); }; FactPeriodResolver.prototype.get = function (propertyName, year, fiscalPeriod) { var _a, _b; this.resolveValues(propertyName, year); var index = (_a = { Q1: 0, Q2: 1, Q3: 2, Q4: 3, FY: 3 }[fiscalPeriod]) !== null && _a !== void 0 ? _a : 4; var isAnnual = fiscalPeriod === 'FY'; var bucket = isAnnual ? this.sumByQuarterByPropertyByYear.get(year) : this.valueByQuarterByPropertyByYear.get(year); return (_b = bucket === null || bucket === void 0 ? void 0 : bucket.get(propertyName)) === null || _b === void 0 ? void 0 : _b.get(index); }; FactPeriodResolver.prototype.getPropertyBuckets = function (year, propertyName) { var _a, _b, _c, _d, _e, _f, _g; var isInstant = !((_a = this.propertiesResolvableByYear.get(year)) === null || _a === void 0 ? void 0 : _a.has(propertyName)); var bucketQuarter = (_c = (_b = this.valueByQuarterByPropertyByYear.get(year)) === null || _b === void 0 ? void 0 : _b.get(propertyName)) !== null && _c !== void 0 ? _c : new Map(); var bucketSum = isInstant ? bucketQuarter : (_e = (_d = this.sumByQuarterByPropertyByYear.get(year)) === null || _d === void 0 ? void 0 : _d.get(propertyName)) !== null && _e !== void 0 ? _e : new Map(); var bucketString = (_g = (_f = this.valueByQuarterByPropertyByYearString.get(year)) === null || _f === void 0 ? void 0 : _f.get(propertyName)) !== null && _g !== void 0 ? _g : new Map(); return { bucketQuarter: bucketQuarter, bucketSum: bucketSum, bucketString: bucketString }; }; /** * 0, 3, 6, 9, or 12 month period. 0 is instantaneous data that doesn't have a time period, (ex: assets) * other facts have values that are for a period of time. (like revenue over the last 6 months). * * Use periods 0 and 3 for quarterly reports and 0 and 12 for annual reports. */ FactPeriodResolver.getPeriod = function (params) { var start = params.start, end = params.end; if (!start || start === end) return 0; var differenceMS = new Date(end).getTime() - new Date(start).getTime(); var differenceDays = differenceMS / 86400000; if (differenceDays < 135) return 3; if (differenceDays < 225) return 6; if (differenceDays < 315) return 9; return 12; }; FactPeriodResolver.prototype.getPeriod = function (params) { return FactPeriodResolver.getPeriod(params); }; FactPeriodResolver.prototype.getOrSetReport = function (params) { var _a; var year = params.year, quarter = params.quarter; var key = "".concat(year, "_").concat(quarter); var report = (_a = this.unresolvedReports.get(key)) !== null && _a !== void 0 ? _a : { cik: this.cik, fiscalYear: year, fiscalPeriod: quarter === 4 ? 'FY' : "Q".concat(quarter), dateReport: '', dateFiled: '', url: null, splitDate: null, splitRatio: null, }; if (!this.unresolvedReports.has(key)) { this.unresolvedReports.set(key, report); } return report; }; /** * True if the filed date is within 60 days of the end date. This indicates that it is likely * the original filing of the fact because filers are required to submit within 45 days of the * period end, and subsequent reports are filed 90 days apart. */ FactPeriodResolver.prototype.isOriginalFiling = function (params) { var end = params.end, filed = params.filed; var DAY_60_MS = 5184000000; return new Date(filed).getTime() - new Date(end).getTime() < DAY_60_MS; }; FactPeriodResolver.prototype.add = function (params) { var year = params.year, value = params.value, propertyName = params.name, quarter = params.quarter, start = params.start, end = params.end, filed = params.filed; var bucketIndex = quarter - 1; var period = this.isAverageShares({ propertyName: propertyName }) ? 0 : this.getPeriod({ start: start, end: end }); this.addPropertyByYear(this.propertiesByYear, year, propertyName); if (typeof value === 'string') { var bucket = this.getOrSetBucketArr(this.valueByQuarterByPropertyByYearString, year, propertyName); if (this.isPreferredValue({ bucket: bucket, bucketIndex: bucketIndex, end: end, filed: filed })) { bucket.set(bucketIndex, value); } return; } if (period === 0) { var bucket = this.getOrSetBucketArr(this.valueByQuarterByPropertyByYear, year, propertyName); var bucketSum = this.getOrSetBucketArr(this.sumByQuarterByPropertyByYear, year, propertyName); if (this.isPreferredValue({ bucket: bucket, bucketIndex: bucketIndex, end: end, filed: filed })) { bucket.set(bucketIndex, value); bucketSum.set(bucketIndex, value); } return; } if (period === 3) { var bucket = this.getOrSetBucketArr(this.valueByQuarterByPropertyByYear, year, propertyName); if (this.isPreferredValue({ bucket: bucket, bucketIndex: bucketIndex, end: end, filed: filed })) { bucket.set(bucketIndex, value); } } if (quarter === 1 || period > 3) { var bucket = this.getOrSetBucketArr(this.sumByQuarterByPropertyByYear, year, propertyName); if (this.isPreferredValue({ bucket: bucket, bucketIndex: bucketIndex, end: end, filed: filed })) { bucket.set(bucketIndex, value); } } this.addPropertyByYear(this.propertiesResolvableByYear, year, propertyName); }; FactPeriodResolver.prototype.buildUnresolvedReports = function () { var _this = this; this.propertiesByYear.forEach(function (properties, year) { properties.forEach(function (propertyName) { var _a; for (var i = 0; i < 4; i++) { var bucketSum = (_a = _this.sumByQuarterByPropertyByYear.get(year)) === null || _a === void 0 ? void 0 : _a.get(propertyName); var value = bucketSum === null || bucketSum === void 0 ? void 0 : bucketSum.get(i); if (value === undefined) continue; var report = _this.getOrSetReport({ year: year, quarter: i + 1 }); report[propertyName] = value; } }); }); }; FactPeriodResolver.prototype.forEach = function (callback) { var _this = this; this.buildUnresolvedReports(); this.propertiesByYear.forEach(function (properties, year) { properties.forEach(function (propertyName) { var _a, _b; _this.resolveValues(propertyName, year); for (var i = 0; i < 4; i++) { var isAnnual = i === 4; var _c = _this.getPropertyBuckets(year, propertyName), bucketQuarter = _c.bucketQuarter, bucketSum = _c.bucketSum, bucketString = _c.bucketString; var valueQuarter = (_a = bucketQuarter.get(i)) !== null && _a !== void 0 ? _a : bucketString.get(i); var valueTrailing = bucketSum.get(i); var value = (_b = (isAnnual ? bucketSum.get(3) : bucketQuarter.get(i))) !== null && _b !== void 0 ? _b : bucketString.get(i); var quarter = i + 1; callback({ year: year, fiscalPeriod: "Q".concat(quarter), propertyName: propertyName, value: value, quarter: quarter, valueQuarter: valueQuarter, valueTrailing: valueTrailing, }); } }); }); }; return FactPeriodResolver; }()); exports.default = FactPeriodResolver;