sec-edgar-api
Version:
Fetch and parse SEC earnings reports and other filings. Useful for financial analysis.
242 lines (241 loc) • 13.5 kB
JavaScript
"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;