sec-edgar-api 0.2.1 → 0.2.2

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.
Files changed (73) hide show
  1. package/README.md +3 -5
  2. package/build/services/DocumentParser/HtmlTableExtractor.d.ts +41 -0
  3. package/build/services/DocumentParser/HtmlTableExtractor.js +408 -0
  4. package/build/services/DocumentParser/XMLParser.d.ts +20 -5
  5. package/build/services/DocumentParser/XMLParser.js +122 -118
  6. package/build/services/DocumentParser/parsers/index.d.ts +5 -3
  7. package/build/services/DocumentParser/parsers/index.js +5 -3
  8. package/build/services/DocumentParser/parsers/parse-current-filings.d.ts +3 -0
  9. package/build/services/DocumentParser/parsers/parse-current-filings.js +98 -0
  10. package/build/services/DocumentParser/parsers/parse-form-13f.d.ts +6 -0
  11. package/build/services/DocumentParser/parsers/parse-form-13f.js +91 -0
  12. package/build/services/DocumentParser/parsers/parse-form-13g.js +2 -2
  13. package/build/services/DocumentParser/parsers/parse-form-4.d.ts +6 -1
  14. package/build/services/DocumentParser/parsers/parse-form-4.js +134 -204
  15. package/build/services/DocumentParser/parsers/parse-form-def14a.d.ts +1 -2
  16. package/build/services/DocumentParser/parsers/parse-form-def14a.js +157 -106
  17. package/build/services/ReportBuilder/FactFiscalCalculator.d.ts +47 -0
  18. package/build/services/ReportBuilder/FactFiscalCalculator.js +228 -0
  19. package/build/services/ReportBuilder/FactPeriodResolver.d.ts +44 -0
  20. package/build/services/ReportBuilder/FactPeriodResolver.js +185 -0
  21. package/build/services/ReportBuilder/FactRecordBuilder.d.ts +7 -0
  22. package/build/services/ReportBuilder/FactRecordBuilder.js +49 -0
  23. package/build/services/ReportBuilder/FactSplitAdjuster.d.ts +39 -0
  24. package/build/services/ReportBuilder/FactSplitAdjuster.js +192 -0
  25. package/build/services/ReportBuilder/ReportBuilder.d.ts +37 -0
  26. package/build/services/ReportBuilder/ReportBuilder.js +180 -0
  27. package/build/services/ReportBuilder/ReportRawResolvable.d.ts +17 -0
  28. package/build/services/ReportBuilder/ReportRawResolvable.js +114 -0
  29. package/build/services/ReportBuilder/index.d.ts +2 -0
  30. package/build/services/ReportBuilder/index.js +4 -0
  31. package/build/services/ReportParser/FactItem.d.ts +66 -0
  32. package/build/services/ReportParser/FactItem.js +50 -0
  33. package/build/services/ReportParser/FactItemFactory.d.ts +22 -0
  34. package/build/services/ReportParser/FactItemFactory.js +150 -0
  35. package/build/services/ReportParser/FactSplitMap.d.ts +16 -0
  36. package/build/services/ReportParser/FactSplitMap.js +101 -0
  37. package/build/services/ReportParser/PropertyResolver.d.ts +1 -0
  38. package/build/services/ReportParser/PropertyResolver.js +1 -0
  39. package/build/services/ReportParser/ReportParser.d.ts +3 -10
  40. package/build/services/ReportParser/ReportParser.js +8 -23
  41. package/build/services/ReportParser/ReportRawParser.d.ts +5 -28
  42. package/build/services/ReportParser/ReportRawParser.js +29 -141
  43. package/build/services/ReportParser/ReportWrapper.js +2 -5
  44. package/build/services/ReportParser/resolvers/index.d.ts +2 -0
  45. package/build/services/ReportParser/resolvers/index.js +2 -0
  46. package/build/services/ReportParser/resolvers/resolve-cash-flow-capex.js +4 -3
  47. package/build/services/ReportParser/resolvers/resolve-cash-flow-operating.js +1 -1
  48. package/build/services/ReportParser/resolvers/resolve-cash-flow-working-capital-non-cash.js +1 -1
  49. package/build/services/ReportParser/resolvers/resolve-expense-depreciation.js +1 -1
  50. package/build/services/ReportParser/resolvers/resolve-fiscal-year-cumulative-properties.js +28 -14
  51. package/build/services/ReportParser/resolvers/resolve-q4-fiscal-year-matching-properties.js +32 -4
  52. package/build/services/ReportParser/resolvers/resolve-split-ratio.d.ts +2 -0
  53. package/build/services/ReportParser/resolvers/resolve-split-ratio.js +37 -0
  54. package/build/services/SecEdgarApi/SecEdgarApi.d.ts +85 -47
  55. package/build/services/SecEdgarApi/SecEdgarApi.js +246 -108
  56. package/build/types/current-filings-xml.type.d.ts +74 -0
  57. package/build/types/current-filings-xml.type.js +6 -0
  58. package/build/types/current-filings.type.d.ts +44 -0
  59. package/build/types/current-filings.type.js +2 -0
  60. package/build/types/form-13f-xml.type.d.ts +105 -0
  61. package/build/types/form-13f-xml.type.js +2 -0
  62. package/build/types/form-4-xml.type.d.ts +132 -0
  63. package/build/types/form-4-xml.type.js +2 -0
  64. package/build/types/index.d.ts +2 -2
  65. package/build/types/index.js +2 -2
  66. package/build/types/parsed-filings.type.d.ts +144 -5
  67. package/build/types/report-raw.type.d.ts +4 -7
  68. package/build/types/report-translated.type.d.ts +1 -2
  69. package/build/types/submission.type.d.ts +3 -2
  70. package/build/util/calculation-map-by-ns.d.ts +6 -0
  71. package/build/util/calculation-map-by-ns.js +9 -0
  72. package/build/util/key-translations.js +1 -2
  73. package/package.json +5 -2
@@ -1,126 +1,177 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.parseFormDef14a = void 0;
4
- var XMLParser_1 = require("../XMLParser");
4
+ var HtmlTableExtractor_1 = require("../HtmlTableExtractor");
5
5
  /**
6
6
  * Form DEF 14a - Proxy Statement
7
7
  *
8
8
  * example at https://www.sec.gov/Archives/edgar/data/320193/000130817923000019/laap2023_def14a.htm
9
9
  */
10
- function parseFormDef14a(params, xmlParser) {
11
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s;
12
- if (xmlParser === void 0) { xmlParser = new XMLParser_1.default(); }
10
+ function parseFormDef14a(params) {
11
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q;
13
12
  var xml = params.xml;
14
- var doc = xmlParser.getDocumentNode({ xml: xml });
15
- var tables = doc.parseTables();
16
- var usedTables = [];
17
- var compensationArr = [];
18
- var holderArr = [];
19
- var findCompensationTables = function (type) {
20
- var tablesFound = tables.filter(function (table) {
21
- var _a, _b, _c, _d, _e;
22
- var hasNameRow = (_a = table.rows[0]) === null || _a === void 0 ? void 0 : _a.some(function (col) { return "".concat(col).toLowerCase().includes('name'); });
23
- var hasTotalRow = (_b = table.rows[0]) === null || _b === void 0 ? void 0 : _b.some(function (col) { return "".concat(col).toLowerCase().includes('total'); });
24
- var hasAwardRow = (_c = table.rows[0]) === null || _c === void 0 ? void 0 : _c.some(function (col) { return "".concat(col).toLowerCase().includes('award'); });
25
- var titleLower = table.title.toLowerCase();
26
- var textLower = (_e = (_d = table.textBefore) === null || _d === void 0 ? void 0 : _d.toLowerCase()) !== null && _e !== void 0 ? _e : '';
27
- var isTitleMatch = titleLower.includes(type) && titleLower.includes('compensation');
28
- var isTextMatch = hasNameRow && hasTotalRow && hasAwardRow && textLower.includes(type);
29
- return (isTitleMatch || isTextMatch) && !usedTables.includes(table);
30
- });
31
- tablesFound.forEach(function (t) { return usedTables.push(t); });
32
- return tablesFound;
33
- };
34
- var tablesHolder = tables.filter(function (table) {
35
- var _a, _b;
36
- var hasNameRow = (_a = table.rows[0]) === null || _a === void 0 ? void 0 : _a.some(function (col) { return "".concat(col).toLowerCase().includes('name'); });
37
- var hasPercent = (_b = table.rows[0]) === null || _b === void 0 ? void 0 : _b.some(function (col) { return "".concat(col).toLowerCase().includes('percent'); });
38
- var titleLower = table.title.toLowerCase();
39
- var isTitleMatch = titleLower.includes('security') && titleLower.includes('owner') && titleLower.includes('beneficial');
40
- return isTitleMatch && hasNameRow && hasPercent;
13
+ var parser = new HtmlTableExtractor_1.default();
14
+ var tables = parser.extractTables(xml, {
15
+ stripHtml: true,
16
+ tagsToExclude: ['sup'],
17
+ stripParenthesis: true,
41
18
  });
42
- var foundHoldersKeys = new Set();
43
- var _loop_1 = function (table) {
44
- var header = table.rows[0];
45
- var getIndex = function (search) { return header.findIndex(function (col) { return "".concat(col).toLowerCase().includes(search); }); };
46
- var indexName = getIndex('name');
47
- var indexPercent = getIndex('percent');
48
- var indexShares = (_b = (_a = table.rows[1]) === null || _a === void 0 ? void 0 : _a.findIndex(function (col) { return typeof col === 'number' && !isNaN(col); })) !== null && _b !== void 0 ? _b : -1;
49
- for (var i = 1; i < table.rows.length; i++) {
50
- for (var i_1 = 1; i_1 < table.rows.length; i_1++) {
51
- var nameVal = (_d = (_c = table.rows[i_1]) === null || _c === void 0 ? void 0 : _c[indexName]) !== null && _d !== void 0 ? _d : null;
52
- if (typeof nameVal !== 'string')
53
- continue;
54
- var nameParts = nameVal.split('}}');
55
- var namePartsSpaces = nameVal.split(' ');
56
- var position = (_e = nameParts[1]) !== null && _e !== void 0 ? _e : namePartsSpaces.slice(2, namePartsSpaces.length).join(' ');
57
- var name_1 = nameParts[1] ? nameParts[0] : namePartsSpaces.slice(0, 2).join(' ');
58
- var holder = {
59
- name: name_1.replace(/{{/g, '').replace(/}}/g, '').trim(),
60
- position: position.replace(/{{/g, '').replace(/}}/g, '').trim() || null,
61
- shares: Number((_f = table.rows[i_1]) === null || _f === void 0 ? void 0 : _f[indexShares]) || null,
62
- percentOfClass: String((_g = table.rows[i_1]) === null || _g === void 0 ? void 0 : _g[indexPercent]) || null,
63
- };
64
- var key = "".concat(holder.name).concat(holder.position).concat(holder.shares).concat(holder.percentOfClass);
65
- if (!foundHoldersKeys.has(key))
66
- holderArr.push(holder);
67
- foundHoldersKeys.add(key);
68
- }
69
- }
70
- };
71
- for (var _i = 0, tablesHolder_1 = tablesHolder; _i < tablesHolder_1.length; _i++) {
72
- var table = tablesHolder_1[_i];
73
- _loop_1(table);
74
- }
75
- for (var _t = 0, _u = ['director', 'executive', 'summary']; _t < _u.length; _t++) {
76
- var type = _u[_t];
77
- var _loop_2 = function (table) {
78
- if (!table)
79
- return "continue";
80
- var header = table.rows[0];
81
- var getIndex = function (search) { return header.findIndex(function (col) { return "".concat(col).toLowerCase().includes(search); }); };
82
- var indexName = getIndex('name');
83
- var indexYear = getIndex('year');
19
+ var compensationTables = [];
20
+ for (var _i = 0, tables_1 = tables; _i < tables_1.length; _i++) {
21
+ var table = tables_1[_i];
22
+ var compensationRows = [];
23
+ var bodyIndex = table.rows.findIndex(function (row) { return row.some(function (col) { return !col.isHeaderRowCell; }); });
24
+ var headerIndex = bodyIndex - 1;
25
+ var textBefore = parser.stripHtml(table.htmlBefore, { tagsToExclude: ['sup'] }).toLowerCase();
26
+ var hasNameRow = (_a = table.rows[headerIndex]) === null || _a === void 0 ? void 0 : _a.some(function (col) { return "".concat(col.valueParsed).toLowerCase().includes('name'); });
27
+ var hasSalaryRow = (_b = table.rows[headerIndex]) === null || _b === void 0 ? void 0 : _b.some(function (col) {
28
+ return "".concat(col.valueParsed).toLowerCase().includes('salary');
29
+ });
30
+ var hasTotalRow = (_c = table.rows[headerIndex]) === null || _c === void 0 ? void 0 : _c.some(function (col) { return "".concat(col.valueParsed).toLowerCase().includes('total'); });
31
+ var isCompensationTable = Boolean(hasNameRow && hasTotalRow && hasSalaryRow);
32
+ if (!isCompensationTable)
33
+ continue;
34
+ var defaultPosition = 'Executive';
35
+ if (textBefore.includes('executive'))
36
+ defaultPosition = 'Executive';
37
+ else if (textBefore.includes('director'))
38
+ defaultPosition = 'Director';
39
+ var _loop_1 = function (i) {
40
+ var row = table.rows[i];
41
+ var getIndex = function (search, isNum, isYear) {
42
+ if (isNum === void 0) { isNum = true; }
43
+ if (isYear === void 0) { isYear = false; }
44
+ return row.findIndex(function (col) {
45
+ var hasType = isNum ? typeof col.valueParsed === 'number' : true;
46
+ var hasSearch = "".concat(col.headerCol).toLowerCase().includes(search);
47
+ var hasYear = !isYear || (Number(col.valueParsed) > 1900 && Number(col.valueParsed) < 2100);
48
+ return hasType && hasSearch && hasYear;
49
+ });
50
+ };
51
+ var indexName = getIndex('name', false);
52
+ var indexYear = getIndex('year', true, true);
84
53
  var indexSalary = getIndex('salary') === -1 ? getIndex('cash') : getIndex('salary');
85
54
  var indexBonus = getIndex('bonus');
86
55
  var indexStock = getIndex('stock');
87
- var indexNonEquity = getIndex('non-equity') === -1 ? getIndex('option') : getIndex('non-equity');
56
+ var indexOption = getIndex('non-equity') === -1 ? getIndex('option') : getIndex('non-equity');
88
57
  var indexOther = getIndex('other');
89
58
  var indexTotal = getIndex('total');
90
- var defaultPosition = {
91
- director: 'Director',
92
- executive: 'Executive',
93
- summary: null,
94
- }[type];
95
- for (var i = 1; i < table.rows.length; i++) {
96
- var nameVal = (_j = (_h = table.rows[i]) === null || _h === void 0 ? void 0 : _h[indexName]) !== null && _j !== void 0 ? _j : null;
97
- if (typeof nameVal !== 'string')
98
- continue;
99
- var nameParts = nameVal.split('}}');
100
- var namePartsSpaces = nameVal.split(' ');
101
- var position = (_k = nameParts[1]) !== null && _k !== void 0 ? _k : namePartsSpaces.slice(2, namePartsSpaces.length).join(' ');
102
- var name_2 = nameParts[1] ? nameParts[0] : namePartsSpaces.slice(0, 2).join(' ');
103
- var compensation = {
104
- name: name_2.replace(/{{/g, '').replace(/}}/g, '').trim(),
105
- position: position.replace(/{{/g, '').replace(/}}/g, '').trim() || (defaultPosition !== null && defaultPosition !== void 0 ? defaultPosition : null),
106
- year: Number((_l = table.rows[i]) === null || _l === void 0 ? void 0 : _l[indexYear]) || null,
107
- salaryDollars: Number((_m = table.rows[i]) === null || _m === void 0 ? void 0 : _m[indexSalary]) || null,
108
- bonusDollars: Number((_o = table.rows[i]) === null || _o === void 0 ? void 0 : _o[indexBonus]) || null,
109
- stockAwardDollars: Number((_p = table.rows[i]) === null || _p === void 0 ? void 0 : _p[indexStock]) || null,
110
- nonEquityDollars: Number((_q = table.rows[i]) === null || _q === void 0 ? void 0 : _q[indexNonEquity]) || null,
111
- otherDollars: Number((_r = table.rows[i]) === null || _r === void 0 ? void 0 : _r[indexOther]) || null,
112
- totalDollars: Number((_s = table.rows[i]) === null || _s === void 0 ? void 0 : _s[indexTotal]) || null,
113
- };
114
- if (compensation.totalDollars !== null) {
115
- compensationArr.push(compensation);
116
- }
117
- }
59
+ var nameHtml = String((_e = (_d = row[indexName]) === null || _d === void 0 ? void 0 : _d.html) !== null && _e !== void 0 ? _e : '');
60
+ var nameParts = nameHtml
61
+ .replace(/\n/g, ' ')
62
+ .replace(/<sup.*?sup>/gi, '')
63
+ .replace(/&.*?;/g, '')
64
+ .split(/<.*?>/g)
65
+ .filter(function (str) { return str.replace(/\W/gi, '').length > 0; })
66
+ .map(function (str) { return str.replace(/(\s|\n|\t)+/g, ' ').trim(); });
67
+ var year = Number((_f = row[indexYear]) === null || _f === void 0 ? void 0 : _f.valueParsed) ||
68
+ Number(String((_g = row[indexYear]) === null || _g === void 0 ? void 0 : _g.valueParsed)
69
+ .split(/\W/g)
70
+ .find(function (str) { return str.length === 4; })) ||
71
+ null;
72
+ var name_1 = String(parser.parseValue(((_h = nameParts[0]) === null || _h === void 0 ? void 0 : _h.trim()) || '', { stripHtml: false, stripParenthesis: true }) || '');
73
+ var position = String(parser.parseValue((_j = nameParts.slice(1)) === null || _j === void 0 ? void 0 : _j.join(' '), { stripHtml: false, stripParenthesis: true }) || '');
74
+ var isValidPosition = position.replace(/\W/gi, '').length > 0 && isNaN(Number(position));
75
+ compensationRows.push({
76
+ name: name_1.replace(/\s+/g, ' '),
77
+ position: isValidPosition ? position || defaultPosition : defaultPosition,
78
+ year: year,
79
+ positionLevel: defaultPosition || 'Executive',
80
+ salaryDollars: Number((_k = row[indexSalary]) === null || _k === void 0 ? void 0 : _k.valueParsed) || null,
81
+ bonusDollars: Number((_l = row[indexBonus]) === null || _l === void 0 ? void 0 : _l.valueParsed) || null,
82
+ stockAwardDollars: Number((_m = row[indexStock]) === null || _m === void 0 ? void 0 : _m.valueParsed) || null,
83
+ nonEquityDollars: Number((_o = row[indexOption]) === null || _o === void 0 ? void 0 : _o.valueParsed) || null,
84
+ otherDollars: Number((_p = row[indexOther]) === null || _p === void 0 ? void 0 : _p.valueParsed) || null,
85
+ totalDollars: Number((_q = row[indexTotal]) === null || _q === void 0 ? void 0 : _q.valueParsed) || null,
86
+ });
118
87
  };
119
- for (var _v = 0, _w = findCompensationTables(type); _v < _w.length; _v++) {
120
- var table = _w[_v];
121
- _loop_2(table);
88
+ for (var i = bodyIndex; i < table.rows.length; i++) {
89
+ _loop_1(i);
122
90
  }
91
+ compensationTables.push(compensationRows);
123
92
  }
124
- return { executiveCompensation: compensationArr, holders: holderArr };
93
+ mergeYearRows(compensationTables);
94
+ var tablesFlat = flattenTables(compensationTables);
95
+ return { executiveCompensation: tablesFlat };
125
96
  }
126
97
  exports.parseFormDef14a = parseFormDef14a;
98
+ function flattenTables(tables) {
99
+ var isDefaultPosition = function (position) { return position === 'Director' || position === 'Executive'; };
100
+ var compensationByNameYear = new Map();
101
+ var resolveProperty = function (a, b) {
102
+ if (a === null && b === null)
103
+ return null;
104
+ if (a === null && b !== null)
105
+ return b;
106
+ if (a !== null && b === null)
107
+ return a;
108
+ if (typeof a === 'number' && typeof b === 'number') {
109
+ return Math.max(a, b);
110
+ }
111
+ if (typeof a === 'string' && typeof b === 'string') {
112
+ return isDefaultPosition(a) ? b : a;
113
+ }
114
+ return b;
115
+ };
116
+ for (var _i = 0, tables_2 = tables; _i < tables_2.length; _i++) {
117
+ var table = tables_2[_i];
118
+ for (var _a = 0, table_1 = table; _a < table_1.length; _a++) {
119
+ var row = table_1[_a];
120
+ var key = "".concat(row.name, "_").concat(row.year);
121
+ var curRow = compensationByNameYear.get(key) || row;
122
+ compensationByNameYear.set(key, curRow);
123
+ for (var key_1 in row) {
124
+ var k = key_1;
125
+ curRow[k] = resolveProperty(curRow[k], row[k]);
126
+ }
127
+ }
128
+ }
129
+ return Array.from(compensationByNameYear.values());
130
+ }
131
+ function mergeYearRows(compensationTables) {
132
+ var _a;
133
+ var isDefaultPosition = function (position) { return position === 'Director' || position === 'Executive'; };
134
+ var positionByName = new Map();
135
+ for (var _i = 0, compensationTables_1 = compensationTables; _i < compensationTables_1.length; _i++) {
136
+ var table = compensationTables_1[_i];
137
+ var uniqueYears = Array.from(new Set(table.map(function (row) { return row.year; }))).filter(Boolean);
138
+ if (uniqueYears.length <= 1)
139
+ continue;
140
+ var isDesc = uniqueYears[0] > uniqueYears[1];
141
+ for (var i = 1; i < table.length; i++) {
142
+ var row = table[i];
143
+ var rowPrev = table[i - 1];
144
+ var year = Number(row.year);
145
+ var yearPrev = Number(rowPrev.year);
146
+ if (yearPrev && !year) {
147
+ year = isDesc ? yearPrev - 1 : yearPrev + 1;
148
+ row.year = year;
149
+ }
150
+ else if (!yearPrev && year) {
151
+ yearPrev = isDesc ? year - 1 : year + 1;
152
+ rowPrev.year = yearPrev;
153
+ }
154
+ var isIncrementYear = isDesc ? year < yearPrev : year > yearPrev;
155
+ var isSamePersonDiffYear = isIncrementYear && year && yearPrev;
156
+ var isSameName = row.name === rowPrev.name;
157
+ var isSamePosition = row.position === rowPrev.position;
158
+ var mergedNamePosition = "".concat(isSameName ? '' : row.name || '', " ").concat(isDefaultPosition(row.position) || isSamePosition ? '' : row.position || '').trim();
159
+ var mergedPosition = isDefaultPosition(rowPrev.position)
160
+ ? mergedNamePosition
161
+ : "".concat(rowPrev.position || '', " ").concat(mergedNamePosition).trim();
162
+ var nameNew = rowPrev.name || row.name;
163
+ var positionNew = ((_a = rowPrev.position) === null || _a === void 0 ? void 0 : _a.includes(mergedNamePosition)) ? rowPrev.position : mergedPosition;
164
+ if (isSamePersonDiffYear) {
165
+ rowPrev.position = positionNew;
166
+ row.position = positionNew;
167
+ rowPrev.name = nameNew;
168
+ row.name = nameNew;
169
+ }
170
+ positionByName.set(row.name, String(row.position));
171
+ }
172
+ for (var _b = 0, table_2 = table; _b < table_2.length; _b++) {
173
+ var row = table_2[_b];
174
+ row.position = positionByName.get(row.name) || row.position;
175
+ }
176
+ }
177
+ }
@@ -0,0 +1,47 @@
1
+ export interface SetReportDatesParams {
2
+ year: number;
3
+ quarter: number;
4
+ end: string;
5
+ filed: string;
6
+ isAnnual: boolean;
7
+ accn: string;
8
+ }
9
+ export default class FactFiscalCalculator {
10
+ private readonly endDateByYear;
11
+ private readonly fiscalsByEndDate;
12
+ private readonly datesByFiscals;
13
+ private readonly endDateCountMap;
14
+ private readonly filedDateCountByEndDate;
15
+ private didResolve;
16
+ add(fact: {
17
+ end: string;
18
+ filed: string;
19
+ frame?: string;
20
+ }): void;
21
+ private getDaysBefore;
22
+ setReportDates(params: SetReportDatesParams): void;
23
+ addAnnualReportDate(reportDate: string): void;
24
+ private isAnnualReportFact;
25
+ private resolve;
26
+ getDatesByYearQuarter(params: {
27
+ year: number;
28
+ quarter: number;
29
+ }): {
30
+ filed: string;
31
+ end: string;
32
+ } | null;
33
+ getFiscalYearQuarter(params: {
34
+ dateStr: string;
35
+ endDateByYear?: Map<number, Date>;
36
+ }): {
37
+ year: number;
38
+ quarter: number;
39
+ };
40
+ static getFiscalYearQuarter(params: {
41
+ dateStr: string;
42
+ endDateByYear: Map<number, Date>;
43
+ }): {
44
+ quarter: number;
45
+ year: number;
46
+ };
47
+ }
@@ -0,0 +1,228 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ var FactFiscalCalculator = /** @class */ (function () {
4
+ function FactFiscalCalculator() {
5
+ this.endDateByYear = new Map();
6
+ this.fiscalsByEndDate = new Map();
7
+ this.datesByFiscals = new Map();
8
+ /// these get cleared after resolve
9
+ this.endDateCountMap = new Map();
10
+ this.filedDateCountByEndDate = new Map();
11
+ this.didResolve = false;
12
+ }
13
+ FactFiscalCalculator.prototype.add = function (fact) {
14
+ var _a, _b, _c;
15
+ var end = fact.end, filed = fact.filed, frame = fact.frame;
16
+ if (this.didResolve) {
17
+ throw new Error('Cannot add fact after resolving');
18
+ }
19
+ if (this.isAnnualReportFact(fact)) {
20
+ this.addAnnualReportDate(fact.end);
21
+ }
22
+ this.endDateCountMap.set(end, ((_a = this.endDateCountMap.get(end)) !== null && _a !== void 0 ? _a : 0) + 1);
23
+ // don't record filed dates for restated facts
24
+ if (this.getDaysBefore(filed, end) < 60) {
25
+ var bucket = (_b = this.filedDateCountByEndDate.get(end)) !== null && _b !== void 0 ? _b : this.filedDateCountByEndDate.set(end, new Map()).get(end);
26
+ bucket.set(filed, ((_c = bucket.get(filed)) !== null && _c !== void 0 ? _c : 0) + 1);
27
+ }
28
+ };
29
+ FactFiscalCalculator.prototype.getDaysBefore = function (dateStrAfter, dateStrBefore) {
30
+ var dateAfter = new Date(dateStrAfter);
31
+ var dateBefore = new Date(dateStrBefore);
32
+ var dayMS = 86400000;
33
+ var differenceMS = dateAfter.getTime() - dateBefore.getTime();
34
+ return differenceMS / dayMS;
35
+ };
36
+ FactFiscalCalculator.prototype.setReportDates = function (params) {
37
+ var year = params.year, quarter = params.quarter, end = params.end, filed = params.filed, isAnnual = params.isAnnual;
38
+ var key = "".concat(year, "_").concat(quarter);
39
+ if (!this.datesByFiscals.has(key)) {
40
+ if (year.toString().length !== 4)
41
+ return;
42
+ this.datesByFiscals.set(key, { filed: filed, end: end });
43
+ }
44
+ var dates = this.datesByFiscals.get(key);
45
+ dates.filed = filed;
46
+ dates.end = end;
47
+ this.filedDateCountByEndDate.set(end, new Map([[filed, Infinity]]));
48
+ this.endDateCountMap.set(end, Infinity);
49
+ if (isAnnual) {
50
+ this.endDateByYear.set(Number(end.split('-', 1)[0]), new Date(end));
51
+ }
52
+ };
53
+ FactFiscalCalculator.prototype.addAnnualReportDate = function (reportDate) {
54
+ var year = Number(reportDate.split('-', 1)[0]);
55
+ var dateEnd = new Date(reportDate);
56
+ this.endDateByYear.set(year, dateEnd);
57
+ };
58
+ FactFiscalCalculator.prototype.isAnnualReportFact = function (params) {
59
+ var end = params.end, start = params.start, filed = params.filed, period = params.period, frame = params.frame;
60
+ if (frame) {
61
+ // FY frames will look like CY2023 vs CY2023Q2 or CY2023Q2I
62
+ return frame.length === 6;
63
+ }
64
+ if (period !== undefined)
65
+ return period === 12;
66
+ // TODO: This was breaking for TSM. they file reports way late.
67
+ if (!start || this.getDaysBefore(end, start) < 315)
68
+ return false;
69
+ return this.getDaysBefore(filed, end) < 60;
70
+ // return start && this.getDaysBefore(end, start) < 315
71
+ };
72
+ FactFiscalCalculator.prototype.resolve = function () {
73
+ var _this = this;
74
+ var _a;
75
+ this.endDateCountMap.forEach(function (count, date) {
76
+ var _a;
77
+ var _b = _this.getFiscalYearQuarter({ dateStr: date }), quarter = _b.quarter, year = _b.year;
78
+ if (year.toString().length !== 4)
79
+ return;
80
+ var key = "".concat(year, "_").concat(quarter);
81
+ if (!_this.datesByFiscals.has(key)) {
82
+ _this.datesByFiscals.set(key, { filed: '', end: '' });
83
+ }
84
+ var dateMap = _this.datesByFiscals.get(key);
85
+ var countPrevDate = (_a = _this.endDateCountMap.get(dateMap.end)) !== null && _a !== void 0 ? _a : 0;
86
+ if (count >= countPrevDate) {
87
+ dateMap.end = date;
88
+ }
89
+ });
90
+ // set to the most commonly used filed date for each end date
91
+ this.filedDateCountByEndDate.forEach(function (countByFiledDate, endDate) {
92
+ var maxCount = 0;
93
+ var filedDate = '';
94
+ countByFiledDate.forEach(function (count, filed) {
95
+ if (count > maxCount) {
96
+ maxCount = count;
97
+ filedDate = filed;
98
+ }
99
+ });
100
+ var _a = _this.getFiscalYearQuarter({ dateStr: endDate }), quarter = _a.quarter, year = _a.year;
101
+ var key = "".concat(year, "_").concat(quarter);
102
+ if (!_this.datesByFiscals.has(key)) {
103
+ if (year.toString().length !== 4)
104
+ return;
105
+ _this.datesByFiscals.set(key, { filed: '', end: '' });
106
+ }
107
+ var dateMap = _this.datesByFiscals.get(key);
108
+ dateMap.filed = filedDate;
109
+ });
110
+ // some reports are missing filed dates, this guesses dates based on other years.
111
+ var monthDayByQuarter = new Map();
112
+ // find dates for each quarter to fill in missing.
113
+ var foundCount = 0;
114
+ for (var _i = 0, _b = Array.from(this.datesByFiscals.entries()); _i < _b.length; _i++) {
115
+ var _c = _b[_i], key = _c[0], dates = _c[1];
116
+ var quarter = Number(key.split('_')[1]);
117
+ var bucket = (_a = monthDayByQuarter.get(quarter)) !== null && _a !== void 0 ? _a : { filedMonthDay: '', endMonthDay: '' };
118
+ monthDayByQuarter.set(quarter, bucket);
119
+ if (dates.filed && !bucket.filedMonthDay) {
120
+ bucket.filedMonthDay = dates.filed.substring(5);
121
+ foundCount++;
122
+ }
123
+ if (dates.end && !bucket.endMonthDay) {
124
+ bucket.endMonthDay = dates.end.substring(5);
125
+ foundCount++;
126
+ }
127
+ if (foundCount === 8) {
128
+ break;
129
+ }
130
+ }
131
+ // if date falls on a weekend, move it to the nearest weekday
132
+ var ensureNoWeekends = function (dateStr) {
133
+ var date = new Date(dateStr);
134
+ if (date.getDay() === 6)
135
+ date.setDate(date.getDate() - 1);
136
+ if (date.getDay() === 0)
137
+ date.setDate(date.getDate() + 1);
138
+ return date.toISOString().split('T')[0];
139
+ };
140
+ // fill in missing dates
141
+ this.datesByFiscals.forEach(function (dates, key) {
142
+ var _a;
143
+ var quarter = Number(key.split('_')[1]);
144
+ var datesFound = (_a = monthDayByQuarter.get(quarter)) !== null && _a !== void 0 ? _a : { filedMonthDay: '', endMonthDay: '' };
145
+ // if filing month comes after report month, it's the same year, otherwise it's the next year
146
+ if (!dates.filed && dates.end && datesFound.filedMonthDay) {
147
+ var _b = dates.end.split('-'), yearEnd = _b[0], month = _b[1], day = _b[2];
148
+ var yearFiled = datesFound.filedMonthDay >= "".concat(month, "-").concat(day) ? yearEnd : Number(yearEnd) + 1;
149
+ dates.filed = ensureNoWeekends("".concat(yearFiled, "-").concat(datesFound.filedMonthDay));
150
+ }
151
+ if (dates.filed && !dates.end && datesFound.endMonthDay) {
152
+ var _c = dates.filed.split('-'), yearFiled = _c[0], month = _c[1], day = _c[2];
153
+ var yearEnd = "".concat(month, "-").concat(day) >= datesFound.endMonthDay ? yearFiled : Number(yearFiled) - 1;
154
+ dates.end = ensureNoWeekends("".concat(yearEnd, "-").concat(datesFound.endMonthDay));
155
+ }
156
+ });
157
+ this.endDateCountMap.clear();
158
+ this.filedDateCountByEndDate.clear();
159
+ this.didResolve = true;
160
+ };
161
+ FactFiscalCalculator.prototype.getDatesByYearQuarter = function (params) {
162
+ var _a;
163
+ if (!this.didResolve)
164
+ this.resolve();
165
+ this.didResolve = true;
166
+ return (_a = this.datesByFiscals.get("".concat(params.year, "_").concat(params.quarter))) !== null && _a !== void 0 ? _a : null;
167
+ };
168
+ FactFiscalCalculator.prototype.getFiscalYearQuarter = function (params) {
169
+ var dateStr = params.dateStr, _a = params.endDateByYear, endDateByYear = _a === void 0 ? this.endDateByYear : _a;
170
+ if (this.fiscalsByEndDate.has(dateStr)) {
171
+ return this.fiscalsByEndDate.get(dateStr);
172
+ }
173
+ var fiscals = FactFiscalCalculator.getFiscalYearQuarter({ dateStr: dateStr, endDateByYear: endDateByYear });
174
+ this.fiscalsByEndDate.set(dateStr, fiscals);
175
+ return fiscals;
176
+ };
177
+ FactFiscalCalculator.getFiscalYearQuarter = function (params) {
178
+ var dateStr = params.dateStr, endDateByYear = params.endDateByYear;
179
+ if (endDateByYear.size === 0) {
180
+ throw new Error('No annual report dates provided');
181
+ }
182
+ var getYearEndDate = function (year) {
183
+ var _a;
184
+ var YEAR_MS = 31536000000;
185
+ var date = endDateByYear.get(year);
186
+ var datePrev = endDateByYear.get(year - 1);
187
+ var dateNext = endDateByYear.get(year + 1);
188
+ if (date)
189
+ return date;
190
+ if (datePrev)
191
+ return new Date(datePrev.getTime() + YEAR_MS);
192
+ if (dateNext)
193
+ return new Date(dateNext.getTime() - YEAR_MS);
194
+ var prevKnownYear = Math.max.apply(Math, Array.from(endDateByYear.keys()));
195
+ var prevKnownDate = endDateByYear.get(prevKnownYear);
196
+ var years = (year - prevKnownYear) * YEAR_MS;
197
+ return new Date(((_a = prevKnownDate === null || prevKnownDate === void 0 ? void 0 : prevKnownDate.getTime()) !== null && _a !== void 0 ? _a : 0) + years);
198
+ };
199
+ var getDaysBefore = function (dateA, dateB) {
200
+ return (dateB.getTime() - dateA.getTime()) / 86400000;
201
+ };
202
+ var getFiscalQuarter = function (daysBeforeYearEnd) {
203
+ var quarter = 1;
204
+ if (daysBeforeYearEnd < 45) {
205
+ quarter = 4;
206
+ }
207
+ else if (daysBeforeYearEnd < 135) {
208
+ quarter = 3;
209
+ }
210
+ else if (daysBeforeYearEnd < 225) {
211
+ quarter = 2;
212
+ }
213
+ return quarter;
214
+ };
215
+ var yearQuarter = Number(dateStr.split('-')[0]);
216
+ var dateQuarter = new Date(dateStr);
217
+ var yearEndDate = getYearEndDate(yearQuarter);
218
+ var daysBefore = getDaysBefore(dateQuarter, yearEndDate);
219
+ var isCurFiscalYear = daysBefore > -30;
220
+ var fiscalYear = isCurFiscalYear ? yearQuarter : yearQuarter + 1;
221
+ var fiscalYearEndDate = isCurFiscalYear ? yearEndDate : getYearEndDate(fiscalYear);
222
+ var daysFromYearEnd = (fiscalYearEndDate.getTime() - dateQuarter.getTime()) / 86400000;
223
+ var quarter = getFiscalQuarter(daysFromYearEnd);
224
+ return { quarter: quarter, year: fiscalYear };
225
+ };
226
+ return FactFiscalCalculator;
227
+ }());
228
+ exports.default = FactFiscalCalculator;
@@ -0,0 +1,44 @@
1
+ import { FiscalPeriod } from '../../types/report-raw.type';
2
+ export default class FactPeriodResolver {
3
+ /** Values for each quarter [numQ1, numQ2, numQ3, numQ4] */
4
+ private readonly valueByQuarterByPropertyByYear;
5
+ /** Same as by quarter but string values */
6
+ private readonly valueByQuarterByPropertyByYearString;
7
+ /** Value sums (each includes sum of previous quarter). last el used for annual report. [sumQ1, sumQ2, sumQ3, sumFY] */
8
+ private readonly sumByQuarterByPropertyByYear;
9
+ /** Which properties have a range that need to be resolved to get quarterly value */
10
+ private readonly propertiesResolvableByYear;
11
+ /** all properties present for each year */
12
+ private readonly propertiesByYear;
13
+ /** prevent values from being added multiple times */
14
+ private readonly resolvedValues;
15
+ private readonly cik;
16
+ constructor(args: {
17
+ cik: number;
18
+ });
19
+ private getOrSetBucketArr;
20
+ private addPropertyByYear;
21
+ resolveValues(propertyName: string, year: number): void;
22
+ get(propertyName: string, year: number, fiscalPeriod: FiscalPeriod): number | undefined;
23
+ private getPropertyBuckets;
24
+ private getPeriod;
25
+ private getOrSetReport;
26
+ add(params: {
27
+ year: number;
28
+ quarter: number;
29
+ start?: string;
30
+ end: string;
31
+ value: number | string;
32
+ name: string;
33
+ dateReport: string;
34
+ dateFiled: string;
35
+ }): void;
36
+ private readonly unresolvedReports;
37
+ private buildUnresolvedReports;
38
+ forEach(callback: (params: {
39
+ year: number;
40
+ fiscalPeriod: FiscalPeriod;
41
+ propertyName: string;
42
+ value: number | string;
43
+ }) => void): void;
44
+ }