sec-edgar-api 0.2.2 → 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.
Files changed (58) hide show
  1. package/README.md +5 -3
  2. package/build/services/DocumentParser/XMLParser.d.ts +5 -20
  3. package/build/services/DocumentParser/XMLParser.js +118 -122
  4. package/build/services/DocumentParser/parsers/index.d.ts +3 -5
  5. package/build/services/DocumentParser/parsers/index.js +3 -5
  6. package/build/services/DocumentParser/parsers/parse-form-13g.js +2 -2
  7. package/build/services/DocumentParser/parsers/parse-form-4.d.ts +1 -6
  8. package/build/services/DocumentParser/parsers/parse-form-4.js +204 -134
  9. package/build/services/DocumentParser/parsers/parse-form-def14a.d.ts +2 -1
  10. package/build/services/DocumentParser/parsers/parse-form-def14a.js +106 -157
  11. package/build/services/ReportBuilder/FactFiscalCalculator.d.ts +4 -0
  12. package/build/services/ReportBuilder/FactFiscalCalculator.js +4 -0
  13. package/build/services/ReportBuilder/FactPeriodResolver.d.ts +6 -2
  14. package/build/services/ReportBuilder/FactPeriodResolver.js +6 -1
  15. package/build/services/ReportBuilder/FactRecordBuilder.d.ts +4 -1
  16. package/build/services/ReportBuilder/FactRecordBuilder.js +26 -4
  17. package/build/services/ReportBuilder/FactSplitAdjuster.d.ts +7 -0
  18. package/build/services/ReportBuilder/FactSplitAdjuster.js +19 -8
  19. package/build/services/ReportBuilder/ReportBuilder.d.ts +3 -0
  20. package/build/services/ReportBuilder/ReportBuilder.js +15 -9
  21. package/build/services/ReportParser/ReportParser.d.ts +3 -3
  22. package/build/services/ReportParser/ReportParser.js +7 -4
  23. package/build/services/ReportParser/ReportRawParser.d.ts +3 -9
  24. package/build/services/ReportParser/ReportRawParser.js +5 -33
  25. package/build/services/ReportParser/ReportWrapper.js +2 -0
  26. package/build/services/ReportParser/resolvers/resolve-fiscal-year-cumulative-properties.js +2 -1
  27. package/build/services/SecEdgarApi/SecEdgarApi.d.ts +45 -84
  28. package/build/services/SecEdgarApi/SecEdgarApi.js +108 -246
  29. package/build/types/index.d.ts +2 -2
  30. package/build/types/index.js +2 -2
  31. package/build/types/parsed-filings.type.d.ts +5 -144
  32. package/build/types/report-raw.type.d.ts +3 -1
  33. package/build/types/report-translated.type.d.ts +4 -1
  34. package/build/types/submission.type.d.ts +2 -3
  35. package/build/util/key-translations.js +5 -1
  36. package/package.json +2 -5
  37. package/build/services/DocumentParser/HtmlTableExtractor.d.ts +0 -41
  38. package/build/services/DocumentParser/HtmlTableExtractor.js +0 -408
  39. package/build/services/DocumentParser/parsers/parse-current-filings.d.ts +0 -3
  40. package/build/services/DocumentParser/parsers/parse-current-filings.js +0 -98
  41. package/build/services/DocumentParser/parsers/parse-form-13f.d.ts +0 -6
  42. package/build/services/DocumentParser/parsers/parse-form-13f.js +0 -91
  43. package/build/services/ReportParser/FactItem.d.ts +0 -66
  44. package/build/services/ReportParser/FactItem.js +0 -50
  45. package/build/services/ReportParser/FactItemFactory.d.ts +0 -22
  46. package/build/services/ReportParser/FactItemFactory.js +0 -150
  47. package/build/services/ReportParser/FactIterator.d.ts +0 -18
  48. package/build/services/ReportParser/FactIterator.js +0 -35
  49. package/build/services/ReportParser/FactSplitMap.d.ts +0 -16
  50. package/build/services/ReportParser/FactSplitMap.js +0 -101
  51. package/build/types/current-filings-xml.type.d.ts +0 -74
  52. package/build/types/current-filings-xml.type.js +0 -6
  53. package/build/types/current-filings.type.d.ts +0 -44
  54. package/build/types/current-filings.type.js +0 -2
  55. package/build/types/form-13f-xml.type.d.ts +0 -105
  56. package/build/types/form-13f-xml.type.js +0 -2
  57. package/build/types/form-4-xml.type.d.ts +0 -132
  58. package/build/types/form-4-xml.type.js +0 -2
@@ -1,177 +1,126 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.parseFormDef14a = void 0;
4
- var HtmlTableExtractor_1 = require("../HtmlTableExtractor");
4
+ var XMLParser_1 = require("../XMLParser");
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) {
11
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q;
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(); }
12
13
  var xml = params.xml;
13
- var parser = new HtmlTableExtractor_1.default();
14
- var tables = parser.extractTables(xml, {
15
- stripHtml: true,
16
- tagsToExclude: ['sup'],
17
- stripParenthesis: true,
18
- });
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');
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);
29
30
  });
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);
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;
41
+ });
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');
53
84
  var indexSalary = getIndex('salary') === -1 ? getIndex('cash') : getIndex('salary');
54
85
  var indexBonus = getIndex('bonus');
55
86
  var indexStock = getIndex('stock');
56
- var indexOption = getIndex('non-equity') === -1 ? getIndex('option') : getIndex('non-equity');
87
+ var indexNonEquity = getIndex('non-equity') === -1 ? getIndex('option') : getIndex('non-equity');
57
88
  var indexOther = getIndex('other');
58
89
  var indexTotal = getIndex('total');
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
- });
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
+ }
87
118
  };
88
- for (var i = bodyIndex; i < table.rows.length; i++) {
89
- _loop_1(i);
119
+ for (var _v = 0, _w = findCompensationTables(type); _v < _w.length; _v++) {
120
+ var table = _w[_v];
121
+ _loop_2(table);
90
122
  }
91
- compensationTables.push(compensationRows);
92
123
  }
93
- mergeYearRows(compensationTables);
94
- var tablesFlat = flattenTables(compensationTables);
95
- return { executiveCompensation: tablesFlat };
124
+ return { executiveCompensation: compensationArr, holders: holderArr };
96
125
  }
97
126
  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
- }
@@ -6,6 +6,10 @@ export interface SetReportDatesParams {
6
6
  isAnnual: boolean;
7
7
  accn: string;
8
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
+ */
9
13
  export default class FactFiscalCalculator {
10
14
  private readonly endDateByYear;
11
15
  private readonly fiscalsByEndDate;
@@ -1,5 +1,9 @@
1
1
  "use strict";
2
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
+ */
3
7
  var FactFiscalCalculator = /** @class */ (function () {
4
8
  function FactFiscalCalculator() {
5
9
  this.endDateByYear = new Map();
@@ -1,4 +1,9 @@
1
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
+ */
2
7
  export default class FactPeriodResolver {
3
8
  /** Values for each quarter [numQ1, numQ2, numQ3, numQ4] */
4
9
  private readonly valueByQuarterByPropertyByYear;
@@ -30,8 +35,7 @@ export default class FactPeriodResolver {
30
35
  end: string;
31
36
  value: number | string;
32
37
  name: string;
33
- dateReport: string;
34
- dateFiled: string;
38
+ filed: string;
35
39
  }): void;
36
40
  private readonly unresolvedReports;
37
41
  private buildUnresolvedReports;
@@ -1,6 +1,11 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
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
+ */
4
9
  var FactPeriodResolver = /** @class */ (function () {
5
10
  function FactPeriodResolver(args) {
6
11
  /** Values for each quarter [numQ1, numQ2, numQ3, numQ4] */
@@ -111,7 +116,7 @@ var FactPeriodResolver = /** @class */ (function () {
111
116
  return report;
112
117
  };
113
118
  FactPeriodResolver.prototype.add = function (params) {
114
- var year = params.year, value = params.value, propertyName = params.name, quarter = params.quarter, start = params.start, end = params.end, dateReport = params.dateReport, dateFiled = params.dateFiled;
119
+ var year = params.year, value = params.value, propertyName = params.name, quarter = params.quarter, start = params.start, end = params.end, filed = params.filed;
115
120
  var period = this.getPeriod({ start: start, end: end });
116
121
  this.addPropertyByYear(this.propertiesByYear, year, propertyName);
117
122
  if (typeof value === 'string') {
@@ -1,7 +1,10 @@
1
1
  import { FactItem } from './ReportBuilder';
2
2
  import { CompanyFactListData } from '../../types';
3
+ /**
4
+ * Builds an array of fact records.
5
+ */
3
6
  export default class FactRecordBuilder {
4
- createFacts(data: CompanyFactListData): {
7
+ createFacts(data: CompanyFactListData, filterDuplicates?: boolean): {
5
8
  facts: FactItem[];
6
9
  };
7
10
  }
@@ -1,11 +1,22 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ /**
4
+ * Builds an array of fact records.
5
+ */
3
6
  var FactRecordBuilder = /** @class */ (function () {
4
7
  function FactRecordBuilder() {
5
8
  }
6
- FactRecordBuilder.prototype.createFacts = function (data) {
9
+ FactRecordBuilder.prototype.createFacts = function (data, filterDuplicates) {
10
+ if (filterDuplicates === void 0) { filterDuplicates = true; }
7
11
  var facts = data.facts, cik = data.cik;
8
- var factItems = [];
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
+ };
9
20
  for (var prefix in facts) {
10
21
  var factByPropertyName = facts[prefix];
11
22
  for (var propertyName in factByPropertyName) {
@@ -15,6 +26,7 @@ var FactRecordBuilder = /** @class */ (function () {
15
26
  for (var _i = 0, factValues_1 = factValues; _i < factValues_1.length; _i++) {
16
27
  var factValue = factValues_1[_i];
17
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 });
18
30
  var name_1 = "".concat(prefix, ":").concat(propertyName);
19
31
  var item = {
20
32
  cik: cik,
@@ -37,12 +49,22 @@ var FactRecordBuilder = /** @class */ (function () {
37
49
  item.frame = frame;
38
50
  if (fy)
39
51
  item.fy = fy;
40
- factItems.push(item);
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
+ }
41
63
  }
42
64
  }
43
65
  }
44
66
  }
45
- return { facts: factItems };
67
+ return { facts: Array.from(factsByKey.values()) };
46
68
  };
47
69
  return FactRecordBuilder;
48
70
  }());
@@ -10,6 +10,10 @@ interface SplitData {
10
10
  type FactItemWithFiscalsNumeric = Omit<FactItemWithFiscals, 'cik' | 'value'> & {
11
11
  value: number;
12
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
+ */
13
17
  export default class FactSplitAdjuster {
14
18
  private readonly splitKey;
15
19
  private readonly splitByFiscalYearAmount;
@@ -22,6 +26,9 @@ export default class FactSplitAdjuster {
22
26
  getSplitsAsc(): SplitData[];
23
27
  isSplitProperty(propertyName: string): boolean;
24
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
+ */
25
32
  private didApplySplit;
26
33
  isSplitAdjustableUnit(unit: string): boolean;
27
34
  isShareRatioUnit(unit: string): boolean;
@@ -11,6 +11,10 @@ var __assign = (this && this.__assign) || function () {
11
11
  return __assign.apply(this, arguments);
12
12
  };
13
13
  Object.defineProperty(exports, "__esModule", { value: true });
14
+ /**
15
+ * Adjust share-based property values for splits. Checks where the split was applied,
16
+ * and adjusts all previously filed share facts accordingly.
17
+ */
14
18
  var FactSplitAdjuster = /** @class */ (function () {
15
19
  function FactSplitAdjuster() {
16
20
  this.splitKey = 'StockholdersEquityNoteStockSplitConversionRatio1';
@@ -84,26 +88,33 @@ var FactSplitAdjuster = /** @class */ (function () {
84
88
  this.sortedSplits = null;
85
89
  }
86
90
  };
91
+ /**
92
+ * TODO: Find a more reliable way of checking if the split has already been applied.
93
+ */
87
94
  FactSplitAdjuster.prototype.didApplySplit = function (params) {
88
95
  var nextValue = params.nextValue, prevValue = params.prevValue, split = params.split, isShareRatio = params.isShareRatio, value = params.value, filed = params.filed;
89
96
  if (filed > split.filed) {
90
97
  return true;
91
98
  }
92
- if (filed < split.firstFiled) {
99
+ var dateFiled = new Date(filed);
100
+ // TODO: adust by adding a year because sometimes the already adjusted facts are filed within the
101
+ // year before the split. this might be because the split is listed under a different property? Look into this...
102
+ if (dateFiled.setFullYear(dateFiled.getFullYear() + 1) < new Date(split.firstFiled).getTime()) {
93
103
  return false;
94
104
  }
95
105
  var valWithSplit = isShareRatio ? value / split.value : value * split.value;
96
- if (nextValue !== null) {
97
- var difference = Math.abs(nextValue - value);
98
- var differenceSplit = Math.abs(nextValue - valWithSplit);
99
- return difference < differenceSplit;
100
- }
106
+ // if we still don't know, see if applying the split puts us closer or further from the prev/next quarter value.
101
107
  if (prevValue !== null) {
102
108
  var difference = Math.abs(prevValue - value);
103
109
  var differenceSplit = Math.abs(prevValue - valWithSplit);
104
110
  return difference < differenceSplit;
105
111
  }
106
- return false;
112
+ if (nextValue !== null) {
113
+ var difference = Math.abs(nextValue - value);
114
+ var differenceSplit = Math.abs(nextValue - valWithSplit);
115
+ return difference < differenceSplit;
116
+ }
117
+ return true;
107
118
  };
108
119
  FactSplitAdjuster.prototype.isSplitAdjustableUnit = function (unit) {
109
120
  return unit.toLowerCase().includes('share');
@@ -175,7 +186,7 @@ var FactSplitAdjuster = /** @class */ (function () {
175
186
  split: split,
176
187
  value: value,
177
188
  });
178
- if (didApplySplit) {
189
+ if (didApplySplit || !split.value) {
179
190
  continue;
180
191
  }
181
192
  if (isShareRatio) {
@@ -25,6 +25,9 @@ export interface FactItemWithFiscals extends FactItem {
25
25
  fiscalPeriod: FiscalPeriod;
26
26
  year: number;
27
27
  }
28
+ /**
29
+ * Builds ReportRaw objects from facts. also applies splits and adjusts for fiscal periods.
30
+ */
28
31
  export default class ReportBuilder {
29
32
  private readonly factRecordBuilder;
30
33
  createFacts(companyFacts: CompanyFactListData): {
@@ -4,6 +4,9 @@ var FactFiscalCalculator_1 = require("./FactFiscalCalculator");
4
4
  var FactPeriodResolver_1 = require("./FactPeriodResolver");
5
5
  var FactRecordBuilder_1 = require("./FactRecordBuilder");
6
6
  var FactSplitAdjuster_1 = require("./FactSplitAdjuster");
7
+ /**
8
+ * Builds ReportRaw objects from facts. also applies splits and adjusts for fiscal periods.
9
+ */
7
10
  var ReportBuilder = /** @class */ (function () {
8
11
  function ReportBuilder() {
9
12
  this.factRecordBuilder = new FactRecordBuilder_1.default();
@@ -12,7 +15,7 @@ var ReportBuilder = /** @class */ (function () {
12
15
  return this.factRecordBuilder.createFacts(companyFacts);
13
16
  };
14
17
  ReportBuilder.prototype.buildReports = function (params) {
15
- var _a, _b, _c, _d, _e, _f, _g;
18
+ var _a, _b, _c, _d, _e;
16
19
  var facts = params.facts, reportDates = params.reportDates;
17
20
  if (facts.length === 0) {
18
21
  return [];
@@ -34,6 +37,7 @@ var ReportBuilder = /** @class */ (function () {
34
37
  var minYear = Infinity;
35
38
  var maxYear = -Infinity;
36
39
  var countByAccnByYearQuarter = new Map();
40
+ var filedByPropertyYearQuarterValue = new Map();
37
41
  for (var _i = 0, facts_1 = facts; _i < facts_1.length; _i++) {
38
42
  var fact = facts_1[_i];
39
43
  var end = fact.end, name_1 = fact.name, unit = fact.unit, segments = fact.segments, start = fact.start, value = fact.value, cik = fact.cik, form = fact.form, filed = fact.filed, accn = fact.accn;
@@ -43,14 +47,16 @@ var ReportBuilder = /** @class */ (function () {
43
47
  var segmentValue = segments === null || segments === void 0 ? void 0 : segments.map(function (seg) { return "".concat(seg.dimension, "_").concat(seg.value); }).join('&');
44
48
  var propertyName = (_a = name_1.split(':').pop()) !== null && _a !== void 0 ? _a : '';
45
49
  var propertyNameWithSegment = propertyName + (segmentValue ? "_".concat(segmentValue) : '');
46
- var _h = factFiscalCalculator.getFiscalYearQuarter({ dateStr: end }), quarter = _h.quarter, year = _h.year;
50
+ var _f = factFiscalCalculator.getFiscalYearQuarter({ dateStr: end }), quarter = _f.quarter, year = _f.year;
47
51
  if (year < minYear)
48
52
  minYear = year;
49
53
  if (year > maxYear)
50
54
  maxYear = year;
51
55
  var splitKey = "".concat(year, "_").concat(value);
56
+ var factFiledKey = "".concat(year, "_").concat(quarter, "_").concat(propertyNameWithSegment, "_").concat(value);
52
57
  var isSplit = factSplitAdjuster.isSplitProperty(propertyName);
53
58
  unitByPropertyName.set(propertyNameWithSegment, unit);
59
+ filedByPropertyYearQuarterValue.set(factFiledKey, filed);
54
60
  if (isSplit && new Date(end) > new Date((_c = (_b = splitDateDataByKey.get(splitKey)) === null || _b === void 0 ? void 0 : _b.end) !== null && _c !== void 0 ? _c : 0)) {
55
61
  splitDateDataByKey.set(splitKey, { end: end, quarter: quarter });
56
62
  }
@@ -63,7 +69,6 @@ var ReportBuilder = /** @class */ (function () {
63
69
  countByAccn.set(accn, ((_e = countByAccn.get(accn)) !== null && _e !== void 0 ? _e : 0) + 1);
64
70
  countByAccnByYearQuarter.set(accnKey, countByAccn);
65
71
  }
66
- var dates = factFiscalCalculator.getDatesByYearQuarter({ quarter: quarter, year: year });
67
72
  factPeriodResolver.add({
68
73
  year: year,
69
74
  start: start,
@@ -71,8 +76,7 @@ var ReportBuilder = /** @class */ (function () {
71
76
  name: propertyNameWithSegment,
72
77
  quarter: quarter,
73
78
  value: value,
74
- dateFiled: (_f = dates === null || dates === void 0 ? void 0 : dates.filed) !== null && _f !== void 0 ? _f : '',
75
- dateReport: (_g = dates === null || dates === void 0 ? void 0 : dates.end) !== null && _g !== void 0 ? _g : '',
79
+ filed: filed,
76
80
  });
77
81
  }
78
82
  countByAccnByYearQuarter.forEach(function (countByAccn, yearQuarter) {
@@ -93,12 +97,12 @@ var ReportBuilder = /** @class */ (function () {
93
97
  var reportsByKey = new Map();
94
98
  // resolves quarterly and annual properties and creates reports
95
99
  factPeriodResolver.forEach(function (data) {
96
- var _a;
100
+ var _a, _b;
97
101
  var fiscalPeriod = data.fiscalPeriod, propertyName = data.propertyName, value = data.value, year = data.year;
98
102
  var key = "".concat(year, "_").concat(fiscalPeriod);
99
103
  var quarter = fiscalPeriod === 'FY' ? 4 : Number(fiscalPeriod[1]);
100
104
  var dates = factFiscalCalculator.getDatesByYearQuarter({ quarter: quarter, year: year });
101
- var _b = dates !== null && dates !== void 0 ? dates : { filed: '', end: '' }, filed = _b.filed, end = _b.end;
105
+ var _c = dates !== null && dates !== void 0 ? dates : { filed: '', end: '' }, filed = _c.filed, end = _c.end;
102
106
  var accessionNumber = accessionByYearQuarter.get("".concat(year, "_").concat(quarter));
103
107
  var accessionNoHyphen = accessionNumber === null || accessionNumber === void 0 ? void 0 : accessionNumber.replace(/-/g, '');
104
108
  var url = accessionNumber
@@ -116,13 +120,15 @@ var ReportBuilder = /** @class */ (function () {
116
120
  splitRatio: null,
117
121
  });
118
122
  }
123
+ var filedKey = "".concat(year, "_").concat(quarter, "_").concat(propertyName, "_").concat(value);
119
124
  // add facts to adjust for splits
120
125
  factSplitAdjuster.add({
121
126
  end: end,
122
- filed: filed,
127
+ // use the original fact filed date instead of the report filed date to know if the fact has been split.
128
+ filed: (_a = filedByPropertyYearQuarterValue.get(filedKey)) !== null && _a !== void 0 ? _a : filed,
123
129
  fiscalPeriod: fiscalPeriod,
124
130
  name: propertyName,
125
- unit: (_a = unitByPropertyName.get(propertyName)) !== null && _a !== void 0 ? _a : '',
131
+ unit: (_b = unitByPropertyName.get(propertyName)) !== null && _b !== void 0 ? _b : '',
126
132
  year: year,
127
133
  value: Number(value),
128
134
  accn: accessionNumber !== null && accessionNumber !== void 0 ? accessionNumber : '',