sec-edgar-api 0.2.1 → 0.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/services/ReportBuilder/FactFiscalCalculator.d.ts +51 -0
- package/build/services/ReportBuilder/FactFiscalCalculator.js +232 -0
- package/build/services/ReportBuilder/FactPeriodResolver.d.ts +48 -0
- package/build/services/ReportBuilder/FactPeriodResolver.js +190 -0
- package/build/services/ReportBuilder/FactRecordBuilder.d.ts +10 -0
- package/build/services/ReportBuilder/FactRecordBuilder.js +71 -0
- package/build/services/ReportBuilder/FactSplitAdjuster.d.ts +46 -0
- package/build/services/ReportBuilder/FactSplitAdjuster.js +203 -0
- package/build/services/ReportBuilder/ReportBuilder.d.ts +40 -0
- package/build/services/ReportBuilder/ReportBuilder.js +186 -0
- package/build/services/ReportBuilder/ReportRawResolvable.d.ts +17 -0
- package/build/services/ReportBuilder/ReportRawResolvable.js +114 -0
- package/build/services/ReportBuilder/index.d.ts +2 -0
- package/build/services/ReportBuilder/index.js +4 -0
- package/build/services/ReportParser/PropertyResolver.d.ts +1 -0
- package/build/services/ReportParser/PropertyResolver.js +1 -0
- package/build/services/ReportParser/ReportParser.d.ts +4 -11
- package/build/services/ReportParser/ReportParser.js +15 -27
- package/build/services/ReportParser/ReportRawParser.d.ts +3 -32
- package/build/services/ReportParser/ReportRawParser.js +6 -146
- package/build/services/ReportParser/ReportWrapper.js +4 -5
- package/build/services/ReportParser/resolvers/index.d.ts +2 -0
- package/build/services/ReportParser/resolvers/index.js +2 -0
- package/build/services/ReportParser/resolvers/resolve-cash-flow-capex.js +4 -3
- package/build/services/ReportParser/resolvers/resolve-cash-flow-operating.js +1 -1
- package/build/services/ReportParser/resolvers/resolve-cash-flow-working-capital-non-cash.js +1 -1
- package/build/services/ReportParser/resolvers/resolve-expense-depreciation.js +1 -1
- package/build/services/ReportParser/resolvers/resolve-fiscal-year-cumulative-properties.js +30 -15
- package/build/services/ReportParser/resolvers/resolve-q4-fiscal-year-matching-properties.js +32 -4
- package/build/services/ReportParser/resolvers/resolve-split-ratio.d.ts +2 -0
- package/build/services/ReportParser/resolvers/resolve-split-ratio.js +37 -0
- package/build/services/SecEdgarApi/SecEdgarApi.d.ts +1 -2
- package/build/types/report-raw.type.d.ts +5 -6
- package/build/types/report-translated.type.d.ts +4 -2
- package/build/util/calculation-map-by-ns.d.ts +6 -0
- package/build/util/calculation-map-by-ns.js +9 -0
- package/build/util/key-translations.js +5 -2
- package/package.json +1 -1
- package/build/services/ReportParser/FactIterator.d.ts +0 -18
- package/build/services/ReportParser/FactIterator.js +0 -35
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
export interface SetReportDatesParams {
|
|
2
|
+
year: number;
|
|
3
|
+
quarter: number;
|
|
4
|
+
end: string;
|
|
5
|
+
filed: string;
|
|
6
|
+
isAnnual: boolean;
|
|
7
|
+
accn: string;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Gets the fiscal period for a given date. does this by checking when the FY end periods are,
|
|
11
|
+
* Then measures the offset from the end date to the next/previous fiscal year end.
|
|
12
|
+
*/
|
|
13
|
+
export default class FactFiscalCalculator {
|
|
14
|
+
private readonly endDateByYear;
|
|
15
|
+
private readonly fiscalsByEndDate;
|
|
16
|
+
private readonly datesByFiscals;
|
|
17
|
+
private readonly endDateCountMap;
|
|
18
|
+
private readonly filedDateCountByEndDate;
|
|
19
|
+
private didResolve;
|
|
20
|
+
add(fact: {
|
|
21
|
+
end: string;
|
|
22
|
+
filed: string;
|
|
23
|
+
frame?: string;
|
|
24
|
+
}): void;
|
|
25
|
+
private getDaysBefore;
|
|
26
|
+
setReportDates(params: SetReportDatesParams): void;
|
|
27
|
+
addAnnualReportDate(reportDate: string): void;
|
|
28
|
+
private isAnnualReportFact;
|
|
29
|
+
private resolve;
|
|
30
|
+
getDatesByYearQuarter(params: {
|
|
31
|
+
year: number;
|
|
32
|
+
quarter: number;
|
|
33
|
+
}): {
|
|
34
|
+
filed: string;
|
|
35
|
+
end: string;
|
|
36
|
+
} | null;
|
|
37
|
+
getFiscalYearQuarter(params: {
|
|
38
|
+
dateStr: string;
|
|
39
|
+
endDateByYear?: Map<number, Date>;
|
|
40
|
+
}): {
|
|
41
|
+
year: number;
|
|
42
|
+
quarter: number;
|
|
43
|
+
};
|
|
44
|
+
static getFiscalYearQuarter(params: {
|
|
45
|
+
dateStr: string;
|
|
46
|
+
endDateByYear: Map<number, Date>;
|
|
47
|
+
}): {
|
|
48
|
+
quarter: number;
|
|
49
|
+
year: number;
|
|
50
|
+
};
|
|
51
|
+
}
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
/**
|
|
4
|
+
* Gets the fiscal period for a given date. does this by checking when the FY end periods are,
|
|
5
|
+
* Then measures the offset from the end date to the next/previous fiscal year end.
|
|
6
|
+
*/
|
|
7
|
+
var FactFiscalCalculator = /** @class */ (function () {
|
|
8
|
+
function FactFiscalCalculator() {
|
|
9
|
+
this.endDateByYear = new Map();
|
|
10
|
+
this.fiscalsByEndDate = new Map();
|
|
11
|
+
this.datesByFiscals = new Map();
|
|
12
|
+
/// these get cleared after resolve
|
|
13
|
+
this.endDateCountMap = new Map();
|
|
14
|
+
this.filedDateCountByEndDate = new Map();
|
|
15
|
+
this.didResolve = false;
|
|
16
|
+
}
|
|
17
|
+
FactFiscalCalculator.prototype.add = function (fact) {
|
|
18
|
+
var _a, _b, _c;
|
|
19
|
+
var end = fact.end, filed = fact.filed, frame = fact.frame;
|
|
20
|
+
if (this.didResolve) {
|
|
21
|
+
throw new Error('Cannot add fact after resolving');
|
|
22
|
+
}
|
|
23
|
+
if (this.isAnnualReportFact(fact)) {
|
|
24
|
+
this.addAnnualReportDate(fact.end);
|
|
25
|
+
}
|
|
26
|
+
this.endDateCountMap.set(end, ((_a = this.endDateCountMap.get(end)) !== null && _a !== void 0 ? _a : 0) + 1);
|
|
27
|
+
// don't record filed dates for restated facts
|
|
28
|
+
if (this.getDaysBefore(filed, end) < 60) {
|
|
29
|
+
var bucket = (_b = this.filedDateCountByEndDate.get(end)) !== null && _b !== void 0 ? _b : this.filedDateCountByEndDate.set(end, new Map()).get(end);
|
|
30
|
+
bucket.set(filed, ((_c = bucket.get(filed)) !== null && _c !== void 0 ? _c : 0) + 1);
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
FactFiscalCalculator.prototype.getDaysBefore = function (dateStrAfter, dateStrBefore) {
|
|
34
|
+
var dateAfter = new Date(dateStrAfter);
|
|
35
|
+
var dateBefore = new Date(dateStrBefore);
|
|
36
|
+
var dayMS = 86400000;
|
|
37
|
+
var differenceMS = dateAfter.getTime() - dateBefore.getTime();
|
|
38
|
+
return differenceMS / dayMS;
|
|
39
|
+
};
|
|
40
|
+
FactFiscalCalculator.prototype.setReportDates = function (params) {
|
|
41
|
+
var year = params.year, quarter = params.quarter, end = params.end, filed = params.filed, isAnnual = params.isAnnual;
|
|
42
|
+
var key = "".concat(year, "_").concat(quarter);
|
|
43
|
+
if (!this.datesByFiscals.has(key)) {
|
|
44
|
+
if (year.toString().length !== 4)
|
|
45
|
+
return;
|
|
46
|
+
this.datesByFiscals.set(key, { filed: filed, end: end });
|
|
47
|
+
}
|
|
48
|
+
var dates = this.datesByFiscals.get(key);
|
|
49
|
+
dates.filed = filed;
|
|
50
|
+
dates.end = end;
|
|
51
|
+
this.filedDateCountByEndDate.set(end, new Map([[filed, Infinity]]));
|
|
52
|
+
this.endDateCountMap.set(end, Infinity);
|
|
53
|
+
if (isAnnual) {
|
|
54
|
+
this.endDateByYear.set(Number(end.split('-', 1)[0]), new Date(end));
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
FactFiscalCalculator.prototype.addAnnualReportDate = function (reportDate) {
|
|
58
|
+
var year = Number(reportDate.split('-', 1)[0]);
|
|
59
|
+
var dateEnd = new Date(reportDate);
|
|
60
|
+
this.endDateByYear.set(year, dateEnd);
|
|
61
|
+
};
|
|
62
|
+
FactFiscalCalculator.prototype.isAnnualReportFact = function (params) {
|
|
63
|
+
var end = params.end, start = params.start, filed = params.filed, period = params.period, frame = params.frame;
|
|
64
|
+
if (frame) {
|
|
65
|
+
// FY frames will look like CY2023 vs CY2023Q2 or CY2023Q2I
|
|
66
|
+
return frame.length === 6;
|
|
67
|
+
}
|
|
68
|
+
if (period !== undefined)
|
|
69
|
+
return period === 12;
|
|
70
|
+
// TODO: This was breaking for TSM. they file reports way late.
|
|
71
|
+
if (!start || this.getDaysBefore(end, start) < 315)
|
|
72
|
+
return false;
|
|
73
|
+
return this.getDaysBefore(filed, end) < 60;
|
|
74
|
+
// return start && this.getDaysBefore(end, start) < 315
|
|
75
|
+
};
|
|
76
|
+
FactFiscalCalculator.prototype.resolve = function () {
|
|
77
|
+
var _this = this;
|
|
78
|
+
var _a;
|
|
79
|
+
this.endDateCountMap.forEach(function (count, date) {
|
|
80
|
+
var _a;
|
|
81
|
+
var _b = _this.getFiscalYearQuarter({ dateStr: date }), quarter = _b.quarter, year = _b.year;
|
|
82
|
+
if (year.toString().length !== 4)
|
|
83
|
+
return;
|
|
84
|
+
var key = "".concat(year, "_").concat(quarter);
|
|
85
|
+
if (!_this.datesByFiscals.has(key)) {
|
|
86
|
+
_this.datesByFiscals.set(key, { filed: '', end: '' });
|
|
87
|
+
}
|
|
88
|
+
var dateMap = _this.datesByFiscals.get(key);
|
|
89
|
+
var countPrevDate = (_a = _this.endDateCountMap.get(dateMap.end)) !== null && _a !== void 0 ? _a : 0;
|
|
90
|
+
if (count >= countPrevDate) {
|
|
91
|
+
dateMap.end = date;
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
// set to the most commonly used filed date for each end date
|
|
95
|
+
this.filedDateCountByEndDate.forEach(function (countByFiledDate, endDate) {
|
|
96
|
+
var maxCount = 0;
|
|
97
|
+
var filedDate = '';
|
|
98
|
+
countByFiledDate.forEach(function (count, filed) {
|
|
99
|
+
if (count > maxCount) {
|
|
100
|
+
maxCount = count;
|
|
101
|
+
filedDate = filed;
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
var _a = _this.getFiscalYearQuarter({ dateStr: endDate }), quarter = _a.quarter, year = _a.year;
|
|
105
|
+
var key = "".concat(year, "_").concat(quarter);
|
|
106
|
+
if (!_this.datesByFiscals.has(key)) {
|
|
107
|
+
if (year.toString().length !== 4)
|
|
108
|
+
return;
|
|
109
|
+
_this.datesByFiscals.set(key, { filed: '', end: '' });
|
|
110
|
+
}
|
|
111
|
+
var dateMap = _this.datesByFiscals.get(key);
|
|
112
|
+
dateMap.filed = filedDate;
|
|
113
|
+
});
|
|
114
|
+
// some reports are missing filed dates, this guesses dates based on other years.
|
|
115
|
+
var monthDayByQuarter = new Map();
|
|
116
|
+
// find dates for each quarter to fill in missing.
|
|
117
|
+
var foundCount = 0;
|
|
118
|
+
for (var _i = 0, _b = Array.from(this.datesByFiscals.entries()); _i < _b.length; _i++) {
|
|
119
|
+
var _c = _b[_i], key = _c[0], dates = _c[1];
|
|
120
|
+
var quarter = Number(key.split('_')[1]);
|
|
121
|
+
var bucket = (_a = monthDayByQuarter.get(quarter)) !== null && _a !== void 0 ? _a : { filedMonthDay: '', endMonthDay: '' };
|
|
122
|
+
monthDayByQuarter.set(quarter, bucket);
|
|
123
|
+
if (dates.filed && !bucket.filedMonthDay) {
|
|
124
|
+
bucket.filedMonthDay = dates.filed.substring(5);
|
|
125
|
+
foundCount++;
|
|
126
|
+
}
|
|
127
|
+
if (dates.end && !bucket.endMonthDay) {
|
|
128
|
+
bucket.endMonthDay = dates.end.substring(5);
|
|
129
|
+
foundCount++;
|
|
130
|
+
}
|
|
131
|
+
if (foundCount === 8) {
|
|
132
|
+
break;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
// if date falls on a weekend, move it to the nearest weekday
|
|
136
|
+
var ensureNoWeekends = function (dateStr) {
|
|
137
|
+
var date = new Date(dateStr);
|
|
138
|
+
if (date.getDay() === 6)
|
|
139
|
+
date.setDate(date.getDate() - 1);
|
|
140
|
+
if (date.getDay() === 0)
|
|
141
|
+
date.setDate(date.getDate() + 1);
|
|
142
|
+
return date.toISOString().split('T')[0];
|
|
143
|
+
};
|
|
144
|
+
// fill in missing dates
|
|
145
|
+
this.datesByFiscals.forEach(function (dates, key) {
|
|
146
|
+
var _a;
|
|
147
|
+
var quarter = Number(key.split('_')[1]);
|
|
148
|
+
var datesFound = (_a = monthDayByQuarter.get(quarter)) !== null && _a !== void 0 ? _a : { filedMonthDay: '', endMonthDay: '' };
|
|
149
|
+
// if filing month comes after report month, it's the same year, otherwise it's the next year
|
|
150
|
+
if (!dates.filed && dates.end && datesFound.filedMonthDay) {
|
|
151
|
+
var _b = dates.end.split('-'), yearEnd = _b[0], month = _b[1], day = _b[2];
|
|
152
|
+
var yearFiled = datesFound.filedMonthDay >= "".concat(month, "-").concat(day) ? yearEnd : Number(yearEnd) + 1;
|
|
153
|
+
dates.filed = ensureNoWeekends("".concat(yearFiled, "-").concat(datesFound.filedMonthDay));
|
|
154
|
+
}
|
|
155
|
+
if (dates.filed && !dates.end && datesFound.endMonthDay) {
|
|
156
|
+
var _c = dates.filed.split('-'), yearFiled = _c[0], month = _c[1], day = _c[2];
|
|
157
|
+
var yearEnd = "".concat(month, "-").concat(day) >= datesFound.endMonthDay ? yearFiled : Number(yearFiled) - 1;
|
|
158
|
+
dates.end = ensureNoWeekends("".concat(yearEnd, "-").concat(datesFound.endMonthDay));
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
this.endDateCountMap.clear();
|
|
162
|
+
this.filedDateCountByEndDate.clear();
|
|
163
|
+
this.didResolve = true;
|
|
164
|
+
};
|
|
165
|
+
FactFiscalCalculator.prototype.getDatesByYearQuarter = function (params) {
|
|
166
|
+
var _a;
|
|
167
|
+
if (!this.didResolve)
|
|
168
|
+
this.resolve();
|
|
169
|
+
this.didResolve = true;
|
|
170
|
+
return (_a = this.datesByFiscals.get("".concat(params.year, "_").concat(params.quarter))) !== null && _a !== void 0 ? _a : null;
|
|
171
|
+
};
|
|
172
|
+
FactFiscalCalculator.prototype.getFiscalYearQuarter = function (params) {
|
|
173
|
+
var dateStr = params.dateStr, _a = params.endDateByYear, endDateByYear = _a === void 0 ? this.endDateByYear : _a;
|
|
174
|
+
if (this.fiscalsByEndDate.has(dateStr)) {
|
|
175
|
+
return this.fiscalsByEndDate.get(dateStr);
|
|
176
|
+
}
|
|
177
|
+
var fiscals = FactFiscalCalculator.getFiscalYearQuarter({ dateStr: dateStr, endDateByYear: endDateByYear });
|
|
178
|
+
this.fiscalsByEndDate.set(dateStr, fiscals);
|
|
179
|
+
return fiscals;
|
|
180
|
+
};
|
|
181
|
+
FactFiscalCalculator.getFiscalYearQuarter = function (params) {
|
|
182
|
+
var dateStr = params.dateStr, endDateByYear = params.endDateByYear;
|
|
183
|
+
if (endDateByYear.size === 0) {
|
|
184
|
+
throw new Error('No annual report dates provided');
|
|
185
|
+
}
|
|
186
|
+
var getYearEndDate = function (year) {
|
|
187
|
+
var _a;
|
|
188
|
+
var YEAR_MS = 31536000000;
|
|
189
|
+
var date = endDateByYear.get(year);
|
|
190
|
+
var datePrev = endDateByYear.get(year - 1);
|
|
191
|
+
var dateNext = endDateByYear.get(year + 1);
|
|
192
|
+
if (date)
|
|
193
|
+
return date;
|
|
194
|
+
if (datePrev)
|
|
195
|
+
return new Date(datePrev.getTime() + YEAR_MS);
|
|
196
|
+
if (dateNext)
|
|
197
|
+
return new Date(dateNext.getTime() - YEAR_MS);
|
|
198
|
+
var prevKnownYear = Math.max.apply(Math, Array.from(endDateByYear.keys()));
|
|
199
|
+
var prevKnownDate = endDateByYear.get(prevKnownYear);
|
|
200
|
+
var years = (year - prevKnownYear) * YEAR_MS;
|
|
201
|
+
return new Date(((_a = prevKnownDate === null || prevKnownDate === void 0 ? void 0 : prevKnownDate.getTime()) !== null && _a !== void 0 ? _a : 0) + years);
|
|
202
|
+
};
|
|
203
|
+
var getDaysBefore = function (dateA, dateB) {
|
|
204
|
+
return (dateB.getTime() - dateA.getTime()) / 86400000;
|
|
205
|
+
};
|
|
206
|
+
var getFiscalQuarter = function (daysBeforeYearEnd) {
|
|
207
|
+
var quarter = 1;
|
|
208
|
+
if (daysBeforeYearEnd < 45) {
|
|
209
|
+
quarter = 4;
|
|
210
|
+
}
|
|
211
|
+
else if (daysBeforeYearEnd < 135) {
|
|
212
|
+
quarter = 3;
|
|
213
|
+
}
|
|
214
|
+
else if (daysBeforeYearEnd < 225) {
|
|
215
|
+
quarter = 2;
|
|
216
|
+
}
|
|
217
|
+
return quarter;
|
|
218
|
+
};
|
|
219
|
+
var yearQuarter = Number(dateStr.split('-')[0]);
|
|
220
|
+
var dateQuarter = new Date(dateStr);
|
|
221
|
+
var yearEndDate = getYearEndDate(yearQuarter);
|
|
222
|
+
var daysBefore = getDaysBefore(dateQuarter, yearEndDate);
|
|
223
|
+
var isCurFiscalYear = daysBefore > -30;
|
|
224
|
+
var fiscalYear = isCurFiscalYear ? yearQuarter : yearQuarter + 1;
|
|
225
|
+
var fiscalYearEndDate = isCurFiscalYear ? yearEndDate : getYearEndDate(fiscalYear);
|
|
226
|
+
var daysFromYearEnd = (fiscalYearEndDate.getTime() - dateQuarter.getTime()) / 86400000;
|
|
227
|
+
var quarter = getFiscalQuarter(daysFromYearEnd);
|
|
228
|
+
return { quarter: quarter, year: fiscalYear };
|
|
229
|
+
};
|
|
230
|
+
return FactFiscalCalculator;
|
|
231
|
+
}());
|
|
232
|
+
exports.default = FactFiscalCalculator;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { FiscalPeriod } from '../../types/report-raw.type';
|
|
2
|
+
/**
|
|
3
|
+
* Resolves quarterly and annual values for a property. This is because filers provide total values for the
|
|
4
|
+
* current fiscal year, rather than values for the individual quarters
|
|
5
|
+
* ex: Net income for Q2 will give 6 months revenue, not 3 month.
|
|
6
|
+
*/
|
|
7
|
+
export default class FactPeriodResolver {
|
|
8
|
+
/** Values for each quarter [numQ1, numQ2, numQ3, numQ4] */
|
|
9
|
+
private readonly valueByQuarterByPropertyByYear;
|
|
10
|
+
/** Same as by quarter but string values */
|
|
11
|
+
private readonly valueByQuarterByPropertyByYearString;
|
|
12
|
+
/** Value sums (each includes sum of previous quarter). last el used for annual report. [sumQ1, sumQ2, sumQ3, sumFY] */
|
|
13
|
+
private readonly sumByQuarterByPropertyByYear;
|
|
14
|
+
/** Which properties have a range that need to be resolved to get quarterly value */
|
|
15
|
+
private readonly propertiesResolvableByYear;
|
|
16
|
+
/** all properties present for each year */
|
|
17
|
+
private readonly propertiesByYear;
|
|
18
|
+
/** prevent values from being added multiple times */
|
|
19
|
+
private readonly resolvedValues;
|
|
20
|
+
private readonly cik;
|
|
21
|
+
constructor(args: {
|
|
22
|
+
cik: number;
|
|
23
|
+
});
|
|
24
|
+
private getOrSetBucketArr;
|
|
25
|
+
private addPropertyByYear;
|
|
26
|
+
resolveValues(propertyName: string, year: number): void;
|
|
27
|
+
get(propertyName: string, year: number, fiscalPeriod: FiscalPeriod): number | undefined;
|
|
28
|
+
private getPropertyBuckets;
|
|
29
|
+
private getPeriod;
|
|
30
|
+
private getOrSetReport;
|
|
31
|
+
add(params: {
|
|
32
|
+
year: number;
|
|
33
|
+
quarter: number;
|
|
34
|
+
start?: string;
|
|
35
|
+
end: string;
|
|
36
|
+
value: number | string;
|
|
37
|
+
name: string;
|
|
38
|
+
filed: string;
|
|
39
|
+
}): void;
|
|
40
|
+
private readonly unresolvedReports;
|
|
41
|
+
private buildUnresolvedReports;
|
|
42
|
+
forEach(callback: (params: {
|
|
43
|
+
year: number;
|
|
44
|
+
fiscalPeriod: FiscalPeriod;
|
|
45
|
+
propertyName: string;
|
|
46
|
+
value: number | string;
|
|
47
|
+
}) => void): void;
|
|
48
|
+
}
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
var ReportRawResolvable_1 = require("./ReportRawResolvable");
|
|
4
|
+
/**
|
|
5
|
+
* Resolves quarterly and annual values for a property. This is because filers provide total values for the
|
|
6
|
+
* current fiscal year, rather than values for the individual quarters
|
|
7
|
+
* ex: Net income for Q2 will give 6 months revenue, not 3 month.
|
|
8
|
+
*/
|
|
9
|
+
var FactPeriodResolver = /** @class */ (function () {
|
|
10
|
+
function FactPeriodResolver(args) {
|
|
11
|
+
/** Values for each quarter [numQ1, numQ2, numQ3, numQ4] */
|
|
12
|
+
this.valueByQuarterByPropertyByYear = new Map();
|
|
13
|
+
/** Same as by quarter but string values */
|
|
14
|
+
this.valueByQuarterByPropertyByYearString = new Map();
|
|
15
|
+
/** Value sums (each includes sum of previous quarter). last el used for annual report. [sumQ1, sumQ2, sumQ3, sumFY] */
|
|
16
|
+
this.sumByQuarterByPropertyByYear = new Map();
|
|
17
|
+
/** Which properties have a range that need to be resolved to get quarterly value */
|
|
18
|
+
this.propertiesResolvableByYear = new Map();
|
|
19
|
+
/** all properties present for each year */
|
|
20
|
+
this.propertiesByYear = new Map();
|
|
21
|
+
/** prevent values from being added multiple times */
|
|
22
|
+
this.resolvedValues = new Set();
|
|
23
|
+
this.unresolvedReports = new Map();
|
|
24
|
+
var cik = args.cik;
|
|
25
|
+
this.cik = cik;
|
|
26
|
+
}
|
|
27
|
+
FactPeriodResolver.prototype.getOrSetBucketArr = function (map, year, propertyName) {
|
|
28
|
+
var _a, _b;
|
|
29
|
+
var propertyMap = (_a = map.get(year)) !== null && _a !== void 0 ? _a : map.set(year, new Map()).get(year);
|
|
30
|
+
return (_b = propertyMap.get(propertyName)) !== null && _b !== void 0 ? _b : propertyMap.set(propertyName, new Map()).get(propertyName);
|
|
31
|
+
};
|
|
32
|
+
FactPeriodResolver.prototype.addPropertyByYear = function (bucket, year, propertyName) {
|
|
33
|
+
var _a;
|
|
34
|
+
var properties = (_a = bucket.get(year)) !== null && _a !== void 0 ? _a : bucket.set(year, new Set()).get(year);
|
|
35
|
+
properties.add(propertyName);
|
|
36
|
+
};
|
|
37
|
+
FactPeriodResolver.prototype.resolveValues = function (propertyName, year) {
|
|
38
|
+
var _a, _b, _c;
|
|
39
|
+
var keyResolved = "".concat(year, "_").concat(propertyName);
|
|
40
|
+
var isResolved = this.resolvedValues.has(keyResolved);
|
|
41
|
+
var isResolvable = Boolean((_a = this.propertiesResolvableByYear.get(year)) === null || _a === void 0 ? void 0 : _a.has(propertyName));
|
|
42
|
+
if (!isResolvable || isResolved)
|
|
43
|
+
return;
|
|
44
|
+
var _d = this.getPropertyBuckets(year, propertyName), bucketQuarter = _d.bucketQuarter, bucketSum = _d.bucketSum;
|
|
45
|
+
if (bucketQuarter.size === 4 && bucketSum.has(3))
|
|
46
|
+
return;
|
|
47
|
+
for (var i = 0; i < 4; i++) {
|
|
48
|
+
var quarterValue = bucketQuarter.get(i);
|
|
49
|
+
var reportKey = "".concat(year, "_").concat(i + 1);
|
|
50
|
+
var unresolvedReport = this.unresolvedReports.get(reportKey);
|
|
51
|
+
var unresolvedReportPrev = this.unresolvedReports.get("".concat(year, "_").concat(i));
|
|
52
|
+
var sumValue = (_b = unresolvedReport === null || unresolvedReport === void 0 ? void 0 : unresolvedReport.getNumber(propertyName)) !== null && _b !== void 0 ? _b : 0;
|
|
53
|
+
var prevSum = (_c = unresolvedReportPrev === null || unresolvedReportPrev === void 0 ? void 0 : unresolvedReportPrev.getNumber(propertyName)) !== null && _c !== void 0 ? _c : 0;
|
|
54
|
+
if (quarterValue === undefined) {
|
|
55
|
+
var bucketQuarter_1 = this.getOrSetBucketArr(this.valueByQuarterByPropertyByYear, year, propertyName);
|
|
56
|
+
bucketQuarter_1.set(i, (Number(sumValue) || 0) - (Number(prevSum) || 0));
|
|
57
|
+
}
|
|
58
|
+
if (quarterValue !== undefined && sumValue === undefined) {
|
|
59
|
+
var bucketSum_1 = this.getOrSetBucketArr(this.sumByQuarterByPropertyByYear, year, propertyName);
|
|
60
|
+
bucketSum_1.set(i, quarterValue + prevSum);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
this.resolvedValues.add(keyResolved);
|
|
64
|
+
};
|
|
65
|
+
FactPeriodResolver.prototype.get = function (propertyName, year, fiscalPeriod) {
|
|
66
|
+
var _a, _b;
|
|
67
|
+
this.resolveValues(propertyName, year);
|
|
68
|
+
var index = (_a = { Q1: 0, Q2: 1, Q3: 2, Q4: 3, FY: 3 }[fiscalPeriod]) !== null && _a !== void 0 ? _a : 4;
|
|
69
|
+
var isAnnual = fiscalPeriod === 'FY';
|
|
70
|
+
var bucket = isAnnual
|
|
71
|
+
? this.sumByQuarterByPropertyByYear.get(year)
|
|
72
|
+
: this.valueByQuarterByPropertyByYear.get(year);
|
|
73
|
+
return (_b = bucket === null || bucket === void 0 ? void 0 : bucket.get(propertyName)) === null || _b === void 0 ? void 0 : _b.get(index);
|
|
74
|
+
};
|
|
75
|
+
FactPeriodResolver.prototype.getPropertyBuckets = function (year, propertyName) {
|
|
76
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
77
|
+
var isInstant = !((_a = this.propertiesResolvableByYear.get(year)) === null || _a === void 0 ? void 0 : _a.has(propertyName));
|
|
78
|
+
var bucketQuarter = (_c = (_b = this.valueByQuarterByPropertyByYear.get(year)) === null || _b === void 0 ? void 0 : _b.get(propertyName)) !== null && _c !== void 0 ? _c : new Map();
|
|
79
|
+
var bucketSum = isInstant
|
|
80
|
+
? bucketQuarter
|
|
81
|
+
: (_e = (_d = this.sumByQuarterByPropertyByYear.get(year)) === null || _d === void 0 ? void 0 : _d.get(propertyName)) !== null && _e !== void 0 ? _e : new Map();
|
|
82
|
+
var bucketString = (_g = (_f = this.valueByQuarterByPropertyByYearString.get(year)) === null || _f === void 0 ? void 0 : _f.get(propertyName)) !== null && _g !== void 0 ? _g : new Map();
|
|
83
|
+
return { bucketQuarter: bucketQuarter, bucketSum: bucketSum, bucketString: bucketString };
|
|
84
|
+
};
|
|
85
|
+
FactPeriodResolver.prototype.getPeriod = function (params) {
|
|
86
|
+
var start = params.start, end = params.end;
|
|
87
|
+
if (!start || start === end)
|
|
88
|
+
return 0;
|
|
89
|
+
var differenceMS = new Date(end).getTime() - new Date(start).getTime();
|
|
90
|
+
var differenceDays = differenceMS / 86400000;
|
|
91
|
+
if (differenceDays < 135)
|
|
92
|
+
return 3;
|
|
93
|
+
if (differenceDays < 225)
|
|
94
|
+
return 6;
|
|
95
|
+
if (differenceDays < 315)
|
|
96
|
+
return 9;
|
|
97
|
+
return 12;
|
|
98
|
+
};
|
|
99
|
+
FactPeriodResolver.prototype.getOrSetReport = function (params) {
|
|
100
|
+
var _a;
|
|
101
|
+
var year = params.year, quarter = params.quarter;
|
|
102
|
+
var key = "".concat(year, "_").concat(quarter);
|
|
103
|
+
var report = (_a = this.unresolvedReports.get(key)) !== null && _a !== void 0 ? _a : new ReportRawResolvable_1.default({
|
|
104
|
+
cik: this.cik,
|
|
105
|
+
fiscalYear: year,
|
|
106
|
+
fiscalPeriod: quarter === 4 ? 'FY' : "Q".concat(quarter),
|
|
107
|
+
dateReport: '',
|
|
108
|
+
dateFiled: '',
|
|
109
|
+
url: null,
|
|
110
|
+
splitDate: null,
|
|
111
|
+
splitRatio: null,
|
|
112
|
+
});
|
|
113
|
+
if (!this.unresolvedReports.has(key)) {
|
|
114
|
+
this.unresolvedReports.set(key, report);
|
|
115
|
+
}
|
|
116
|
+
return report;
|
|
117
|
+
};
|
|
118
|
+
FactPeriodResolver.prototype.add = function (params) {
|
|
119
|
+
var year = params.year, value = params.value, propertyName = params.name, quarter = params.quarter, start = params.start, end = params.end, filed = params.filed;
|
|
120
|
+
var period = this.getPeriod({ start: start, end: end });
|
|
121
|
+
this.addPropertyByYear(this.propertiesByYear, year, propertyName);
|
|
122
|
+
if (typeof value === 'string') {
|
|
123
|
+
var bucket = this.getOrSetBucketArr(this.valueByQuarterByPropertyByYearString, year, propertyName);
|
|
124
|
+
bucket.set(quarter - 1, value);
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
if (period === 0) {
|
|
128
|
+
var bucket = this.getOrSetBucketArr(this.valueByQuarterByPropertyByYear, year, propertyName);
|
|
129
|
+
var bucketSum = this.getOrSetBucketArr(this.sumByQuarterByPropertyByYear, year, propertyName);
|
|
130
|
+
bucket.set(quarter - 1, value);
|
|
131
|
+
bucketSum.set(quarter - 1, value);
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
if (period === 3) {
|
|
135
|
+
var bucket = this.getOrSetBucketArr(this.valueByQuarterByPropertyByYear, year, propertyName);
|
|
136
|
+
bucket.set(quarter - 1, value);
|
|
137
|
+
}
|
|
138
|
+
if (quarter === 1 || period > 3) {
|
|
139
|
+
var bucket = this.getOrSetBucketArr(this.sumByQuarterByPropertyByYear, year, propertyName);
|
|
140
|
+
bucket.set(quarter - 1, value);
|
|
141
|
+
}
|
|
142
|
+
this.addPropertyByYear(this.propertiesResolvableByYear, year, propertyName);
|
|
143
|
+
};
|
|
144
|
+
FactPeriodResolver.prototype.buildUnresolvedReports = function () {
|
|
145
|
+
var _this = this;
|
|
146
|
+
this.propertiesByYear.forEach(function (properties, year) {
|
|
147
|
+
properties.forEach(function (propertyName) {
|
|
148
|
+
var _a, _b;
|
|
149
|
+
for (var i = 0; i < 4; i++) {
|
|
150
|
+
var bucketSum = (_a = _this.sumByQuarterByPropertyByYear.get(year)) === null || _a === void 0 ? void 0 : _a.get(propertyName);
|
|
151
|
+
var bucketQuarter = _this.getOrSetBucketArr(_this.valueByQuarterByPropertyByYear, year, propertyName);
|
|
152
|
+
if (bucketSum && !bucketSum.has(i)) {
|
|
153
|
+
var prevSum = 0;
|
|
154
|
+
var quarterSum = 0;
|
|
155
|
+
for (var j = 0; j < i; j++) {
|
|
156
|
+
prevSum = quarterSum;
|
|
157
|
+
quarterSum = (_b = bucketSum.get(j)) !== null && _b !== void 0 ? _b : quarterSum;
|
|
158
|
+
}
|
|
159
|
+
bucketSum.set(i, quarterSum);
|
|
160
|
+
bucketQuarter.set(i, quarterSum - prevSum);
|
|
161
|
+
}
|
|
162
|
+
var value = bucketSum === null || bucketSum === void 0 ? void 0 : bucketSum.get(i);
|
|
163
|
+
if (value === undefined)
|
|
164
|
+
continue;
|
|
165
|
+
var report = _this.getOrSetReport({ year: year, quarter: i + 1 });
|
|
166
|
+
report.report[propertyName] = value;
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
});
|
|
170
|
+
};
|
|
171
|
+
FactPeriodResolver.prototype.forEach = function (callback) {
|
|
172
|
+
var _this = this;
|
|
173
|
+
this.buildUnresolvedReports();
|
|
174
|
+
this.propertiesByYear.forEach(function (properties, year) {
|
|
175
|
+
properties.forEach(function (propertyName) {
|
|
176
|
+
var _a;
|
|
177
|
+
_this.resolveValues(propertyName, year);
|
|
178
|
+
for (var i = 0; i < 5; i++) {
|
|
179
|
+
var isAnnual = i === 4;
|
|
180
|
+
var _b = _this.getPropertyBuckets(year, propertyName), bucketQuarter = _b.bucketQuarter, bucketSum = _b.bucketSum, bucketString = _b.bucketString;
|
|
181
|
+
var value = (_a = (isAnnual ? bucketSum.get(3) : bucketQuarter.get(i))) !== null && _a !== void 0 ? _a : bucketString.get(i);
|
|
182
|
+
var fiscalPeriod = isAnnual ? 'FY' : "Q".concat(i + 1);
|
|
183
|
+
callback({ year: year, fiscalPeriod: fiscalPeriod, propertyName: propertyName, value: value });
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
});
|
|
187
|
+
};
|
|
188
|
+
return FactPeriodResolver;
|
|
189
|
+
}());
|
|
190
|
+
exports.default = FactPeriodResolver;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { FactItem } from './ReportBuilder';
|
|
2
|
+
import { CompanyFactListData } from '../../types';
|
|
3
|
+
/**
|
|
4
|
+
* Builds an array of fact records.
|
|
5
|
+
*/
|
|
6
|
+
export default class FactRecordBuilder {
|
|
7
|
+
createFacts(data: CompanyFactListData, filterDuplicates?: boolean): {
|
|
8
|
+
facts: FactItem[];
|
|
9
|
+
};
|
|
10
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
/**
|
|
4
|
+
* Builds an array of fact records.
|
|
5
|
+
*/
|
|
6
|
+
var FactRecordBuilder = /** @class */ (function () {
|
|
7
|
+
function FactRecordBuilder() {
|
|
8
|
+
}
|
|
9
|
+
FactRecordBuilder.prototype.createFacts = function (data, filterDuplicates) {
|
|
10
|
+
if (filterDuplicates === void 0) { filterDuplicates = true; }
|
|
11
|
+
var facts = data.facts, cik = data.cik;
|
|
12
|
+
var factsByKey = new Map();
|
|
13
|
+
var keyIndex = 0;
|
|
14
|
+
var createKey = function (params) {
|
|
15
|
+
if (!filterDuplicates)
|
|
16
|
+
return keyIndex++;
|
|
17
|
+
var start = params.start, end = params.end, filed = params.filed, propertyName = params.propertyName;
|
|
18
|
+
return "".concat(start !== null && start !== void 0 ? start : '', "_").concat(end, "_").concat(filed, "_").concat(propertyName);
|
|
19
|
+
};
|
|
20
|
+
for (var prefix in facts) {
|
|
21
|
+
var factByPropertyName = facts[prefix];
|
|
22
|
+
for (var propertyName in factByPropertyName) {
|
|
23
|
+
var units = factByPropertyName[propertyName].units;
|
|
24
|
+
for (var unit in units) {
|
|
25
|
+
var factValues = units[unit];
|
|
26
|
+
for (var _i = 0, factValues_1 = factValues; _i < factValues_1.length; _i++) {
|
|
27
|
+
var factValue = factValues_1[_i];
|
|
28
|
+
var end = factValue.end, start = factValue.start, val = factValue.val, filed = factValue.filed, accn = factValue.accn, form = factValue.form, fp = factValue.fp, frame = factValue.frame, fy = factValue.fy;
|
|
29
|
+
var mapKey = createKey({ propertyName: propertyName, end: end, filed: filed, start: start });
|
|
30
|
+
var name_1 = "".concat(prefix, ":").concat(propertyName);
|
|
31
|
+
var item = {
|
|
32
|
+
cik: cik,
|
|
33
|
+
end: end,
|
|
34
|
+
filed: filed,
|
|
35
|
+
name: name_1,
|
|
36
|
+
unit: unit,
|
|
37
|
+
value: val,
|
|
38
|
+
accn: accn,
|
|
39
|
+
};
|
|
40
|
+
if (start)
|
|
41
|
+
item.start = start;
|
|
42
|
+
if (accn)
|
|
43
|
+
item.accn = accn;
|
|
44
|
+
if (form)
|
|
45
|
+
item.form = form;
|
|
46
|
+
if (fp)
|
|
47
|
+
item.fp = fp;
|
|
48
|
+
if (frame)
|
|
49
|
+
item.frame = frame;
|
|
50
|
+
if (fy)
|
|
51
|
+
item.fy = fy;
|
|
52
|
+
var prevFact = factsByKey.get(mapKey);
|
|
53
|
+
if (!prevFact) {
|
|
54
|
+
factsByKey.set(mapKey, item);
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
// use whichever is closer to the report end date
|
|
58
|
+
var shouldPush = new Date(item.filed).getTime() - new Date(item.end).getTime() <
|
|
59
|
+
new Date(prevFact.filed).getTime() - new Date(prevFact.end).getTime();
|
|
60
|
+
if (shouldPush) {
|
|
61
|
+
factsByKey.set(mapKey, item);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return { facts: Array.from(factsByKey.values()) };
|
|
68
|
+
};
|
|
69
|
+
return FactRecordBuilder;
|
|
70
|
+
}());
|
|
71
|
+
exports.default = FactRecordBuilder;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { FiscalPeriod } from '../../types/report-raw.type';
|
|
2
|
+
import { FactItemWithFiscals } from './ReportBuilder';
|
|
3
|
+
interface SplitData {
|
|
4
|
+
end: string;
|
|
5
|
+
filed: string;
|
|
6
|
+
value: number;
|
|
7
|
+
firstFiled: string;
|
|
8
|
+
fiscalYear: number;
|
|
9
|
+
}
|
|
10
|
+
type FactItemWithFiscalsNumeric = Omit<FactItemWithFiscals, 'cik' | 'value'> & {
|
|
11
|
+
value: number;
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* Adjust share-based property values for splits. Checks where the split was applied,
|
|
15
|
+
* and adjusts all previously filed share facts accordingly.
|
|
16
|
+
*/
|
|
17
|
+
export default class FactSplitAdjuster {
|
|
18
|
+
private readonly splitKey;
|
|
19
|
+
private readonly splitByFiscalYearAmount;
|
|
20
|
+
private readonly factsByYearQuarterByPropertyName;
|
|
21
|
+
private readonly factsAnnaulByYearByPropertyName;
|
|
22
|
+
private readonly resolvedProperties;
|
|
23
|
+
private sortedSplits;
|
|
24
|
+
private getMap;
|
|
25
|
+
add(fact: FactItemWithFiscalsNumeric): void;
|
|
26
|
+
getSplitsAsc(): SplitData[];
|
|
27
|
+
isSplitProperty(propertyName: string): boolean;
|
|
28
|
+
addSplitData(data: Omit<SplitData, 'firstFiled'>): void;
|
|
29
|
+
/**
|
|
30
|
+
* TODO: Find a more reliable way of checking if the split has already been applied.
|
|
31
|
+
*/
|
|
32
|
+
private didApplySplit;
|
|
33
|
+
isSplitAdjustableUnit(unit: string): boolean;
|
|
34
|
+
isShareRatioUnit(unit: string): boolean;
|
|
35
|
+
private getSortedFacts;
|
|
36
|
+
resolveProperty(propertyName: string): void;
|
|
37
|
+
get(propertyName: string, year: number, fiscalPeriod: FiscalPeriod): number | undefined;
|
|
38
|
+
forEach(callback: (params: {
|
|
39
|
+
propertyName: string;
|
|
40
|
+
year: number;
|
|
41
|
+
fiscalPeriod: FiscalPeriod;
|
|
42
|
+
value: number;
|
|
43
|
+
}) => void): void;
|
|
44
|
+
private adjustValuesForSplits;
|
|
45
|
+
}
|
|
46
|
+
export {};
|