sec-edgar-api 0.2.4 → 0.3.0

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 (35) hide show
  1. package/README.md +1 -0
  2. package/build/services/ReportParser/ReportParser.d.ts +6 -13
  3. package/build/services/ReportParser/ReportParser.js +15 -22
  4. package/build/services/ReportRawBuilder/FactGrouper.d.ts +27 -0
  5. package/build/services/ReportRawBuilder/FactGrouper.js +206 -0
  6. package/build/services/{ReportBuilder → ReportRawBuilder}/FactPeriodResolver.d.ts +46 -2
  7. package/build/services/{ReportBuilder → ReportRawBuilder}/FactPeriodResolver.js +81 -20
  8. package/build/services/ReportRawBuilder/FactRecordBuilder.d.ts +9 -0
  9. package/build/services/{ReportBuilder → ReportRawBuilder}/FactRecordBuilder.js +6 -24
  10. package/build/services/ReportRawBuilder/FactSplitAdjuster.d.ts +43 -0
  11. package/build/services/ReportRawBuilder/FactSplitAdjuster.js +242 -0
  12. package/build/services/ReportRawBuilder/ReportRawBuilder.d.ts +39 -0
  13. package/build/services/ReportRawBuilder/ReportRawBuilder.js +158 -0
  14. package/build/services/ReportRawBuilder/index.d.ts +2 -0
  15. package/build/services/ReportRawBuilder/index.js +4 -0
  16. package/build/services/SecEdgarApi/SecEdgarApi.d.ts +10 -2
  17. package/build/services/SecEdgarApi/SecEdgarApi.js +29 -11
  18. package/build/types/common.type.d.ts +41 -0
  19. package/build/types/company-facts.type.d.ts +29 -2
  20. package/package.json +1 -1
  21. package/build/services/ReportBuilder/FactRecordBuilder.d.ts +0 -10
  22. package/build/services/ReportBuilder/FactSplitAdjuster.d.ts +0 -50
  23. package/build/services/ReportBuilder/FactSplitAdjuster.js +0 -214
  24. package/build/services/ReportBuilder/ReportBuilder.d.ts +0 -40
  25. package/build/services/ReportBuilder/ReportBuilder.js +0 -200
  26. package/build/services/ReportBuilder/ReportRawResolvable.d.ts +0 -17
  27. package/build/services/ReportBuilder/ReportRawResolvable.js +0 -114
  28. package/build/services/ReportBuilder/index.d.ts +0 -2
  29. package/build/services/ReportBuilder/index.js +0 -4
  30. package/build/services/ReportParser/ReportRawParser.d.ts +0 -5
  31. package/build/services/ReportParser/ReportRawParser.js +0 -14
  32. package/build/util/calculation-map-by-ns.d.ts +0 -6
  33. package/build/util/calculation-map-by-ns.js +0 -9
  34. /package/build/services/{ReportBuilder → ReportRawBuilder}/FactFiscalCalculator.d.ts +0 -0
  35. /package/build/services/{ReportBuilder → ReportRawBuilder}/FactFiscalCalculator.js +0 -0
package/README.md CHANGED
@@ -149,6 +149,7 @@ Files for mapping & resolving properties:
149
149
 
150
150
  ### Resources
151
151
 
152
+ - XBRL Taxonomies: https://xbrl.us/xbrl-taxonomy/2024-us-gaap/
152
153
  - Validate resolved values: https://finance.yahoo.com/
153
154
  - Financial calculations: https://www.gurufocus.com/
154
155
  - Calculate change in working capital: https://www.oldschoolvalue.com/stock-valuation/change-in-working-capital/
@@ -1,9 +1,10 @@
1
1
  import { CompanyFactListData, ReportRaw, ReportTranslated } from '../../types';
2
+ import ReportRawBuilder from '../ReportRawBuilder';
3
+ import { GetReportsRawParams } from '../SecEdgarApi';
2
4
  import PropertyResolver from './PropertyResolver';
3
- import ReportRawParser from './ReportRawParser';
4
5
  import ReportWrapper from './ReportWrapper';
5
6
  interface ReportParserArgs {
6
- reportRawParser?: ReportRawParser;
7
+ reportBuilder?: ReportRawBuilder;
7
8
  propertyResolver?: PropertyResolver;
8
9
  keyTranslator?: Record<string, string[]>;
9
10
  }
@@ -12,28 +13,20 @@ type TranslateRawReportsCallback<T> = (report: T extends undefined ? ReportTrans
12
13
  * Takes company facts data from the SEC and translates them to reports as json objects.
13
14
  */
14
15
  export default class ReportParser {
15
- private readonly reportRawParser;
16
16
  private readonly keyTranslator;
17
17
  private readonly propertyResolver;
18
+ private readonly reportBuilder;
18
19
  constructor(args?: ReportParserArgs);
19
- /**
20
- * translates company facts to ReportTranslated. To translate to custom report, use parseReportsRaw and translateReportsRaw
21
- *
22
- * This includes only 10-K and 10-Q annual and quarterly reports. To include all reports, use parseReportsRaw
23
- *
24
- * @param companyFactListData This is the json file contents of CIK[number].json file from the SEC website. You can find these using their API or by downloading the companyfacts.zip file: https://www.sec.gov/edgar/sec-api-documentation
25
- */
26
- parseReports(companyFactListData: CompanyFactListData, usePropertyResolver?: boolean): ReportWrapper[];
27
20
  /**
28
21
  * Same as parseReports but accepts ReportRaw[] instead of CompanyFactListData
29
22
  */
30
23
  parseReportsFromRaw(reportsRaw: ReportRaw[], usePropertyResolver?: boolean): ReportWrapper[];
31
24
  /**
32
- * Note that this includes all reports by default, not just annual and quarterly. use options.reportsToInclude to filter
25
+ * Parse raw reports
33
26
  *
34
27
  * @see https://www.sec.gov/edgar/sec-api-documentation
35
28
  */
36
- parseReportsRaw(companyFactListData: CompanyFactListData): ReportRaw[];
29
+ parseReportsRaw(companyFactListData: CompanyFactListData, options?: Omit<GetReportsRawParams, 'symbol'>): ReportRaw[];
37
30
  /**
38
31
  * parseReportsRaw but removes meta data from the report
39
32
  */
@@ -1,32 +1,19 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  var key_translations_1 = require("../../util/key-translations");
4
- var ReportRawResolvable_1 = require("../ReportBuilder/ReportRawResolvable");
4
+ var ReportRawBuilder_1 = require("../ReportRawBuilder");
5
5
  var PropertyResolver_1 = require("./PropertyResolver");
6
- var ReportRawParser_1 = require("./ReportRawParser");
7
6
  var ReportWrapper_1 = require("./ReportWrapper");
8
7
  /**
9
8
  * Takes company facts data from the SEC and translates them to reports as json objects.
10
9
  */
11
10
  var ReportParser = /** @class */ (function () {
12
11
  function ReportParser(args) {
13
- var _a = args !== null && args !== void 0 ? args : {}, _b = _a.reportRawParser, reportRawParser = _b === void 0 ? new ReportRawParser_1.default() : _b, _c = _a.propertyResolver, propertyResolver = _c === void 0 ? new PropertyResolver_1.default() : _c, _d = _a.keyTranslator, keyTranslator = _d === void 0 ? key_translations_1.default : _d;
14
- this.reportRawParser = reportRawParser;
12
+ var _a = args !== null && args !== void 0 ? args : {}, _b = _a.propertyResolver, propertyResolver = _b === void 0 ? new PropertyResolver_1.default() : _b, _c = _a.reportBuilder, reportBuilder = _c === void 0 ? new ReportRawBuilder_1.default() : _c, _d = _a.keyTranslator, keyTranslator = _d === void 0 ? key_translations_1.default : _d;
15
13
  this.keyTranslator = keyTranslator;
16
14
  this.propertyResolver = propertyResolver;
15
+ this.reportBuilder = reportBuilder;
17
16
  }
18
- /**
19
- * translates company facts to ReportTranslated. To translate to custom report, use parseReportsRaw and translateReportsRaw
20
- *
21
- * This includes only 10-K and 10-Q annual and quarterly reports. To include all reports, use parseReportsRaw
22
- *
23
- * @param companyFactListData This is the json file contents of CIK[number].json file from the SEC website. You can find these using their API or by downloading the companyfacts.zip file: https://www.sec.gov/edgar/sec-api-documentation
24
- */
25
- ReportParser.prototype.parseReports = function (companyFactListData, usePropertyResolver) {
26
- if (usePropertyResolver === void 0) { usePropertyResolver = true; }
27
- var reportsRaw = this.reportRawParser.parseReports(companyFactListData);
28
- return this.parseReportsFromRaw(reportsRaw, usePropertyResolver);
29
- };
30
17
  /**
31
18
  * Same as parseReports but accepts ReportRaw[] instead of CompanyFactListData
32
19
  */
@@ -47,12 +34,19 @@ var ReportParser = /** @class */ (function () {
47
34
  return reportWrappers;
48
35
  };
49
36
  /**
50
- * Note that this includes all reports by default, not just annual and quarterly. use options.reportsToInclude to filter
37
+ * Parse raw reports
51
38
  *
52
39
  * @see https://www.sec.gov/edgar/sec-api-documentation
53
40
  */
54
- ReportParser.prototype.parseReportsRaw = function (companyFactListData) {
55
- return this.reportRawParser.parseReports(companyFactListData);
41
+ ReportParser.prototype.parseReportsRaw = function (companyFactListData, options) {
42
+ if (options === void 0) { options = {}; }
43
+ var adjustForSplits = options.adjustForSplits, resolvePeriodValues = options.resolvePeriodValues, includeNamePrefix = options.includeNamePrefix;
44
+ var facts = this.reportBuilder.createFacts(companyFactListData, includeNamePrefix).facts;
45
+ return this.reportBuilder.buildReports({
46
+ facts: facts,
47
+ adjustForSplits: adjustForSplits,
48
+ resolvePeriodValues: resolvePeriodValues,
49
+ });
56
50
  };
57
51
  /**
58
52
  * parseReportsRaw but removes meta data from the report
@@ -82,14 +76,13 @@ var ReportParser = /** @class */ (function () {
82
76
  var reports = [];
83
77
  reportsRaw.forEach(function (report) {
84
78
  var reportNew = {};
85
- var reportRaw = new ReportRawResolvable_1.default(report);
86
79
  // iterate translation keys, ensuring same order and priority
87
80
  for (var key in keyTranslations) {
88
81
  var keysRaw = keyTranslations[key];
89
82
  reportNew[key] = null;
90
83
  for (var _i = 0, keysRaw_1 = keysRaw; _i < keysRaw_1.length; _i++) {
91
84
  var keyRaw = keysRaw_1[_i];
92
- var value = reportRaw.get(keyRaw);
85
+ var value = report[keyRaw];
93
86
  if (value === undefined)
94
87
  continue;
95
88
  reportNew[key] = value;
@@ -97,7 +90,7 @@ var ReportParser = /** @class */ (function () {
97
90
  }
98
91
  }
99
92
  var reportFiltered = callback
100
- ? callback(reportNew, reportRaw.report, keyTranslations)
93
+ ? callback(reportNew, report, keyTranslations)
101
94
  : reportNew;
102
95
  reports.push(reportFiltered);
103
96
  });
@@ -0,0 +1,27 @@
1
+ import { FactItem, FactGroup, SplitData } from '../../types';
2
+ import FactFiscalCalculator from './FactFiscalCalculator';
3
+ /**
4
+ * There are many facts properties for the same period but filed at different times.
5
+ * This groups those together and resolves the period and trailing values for each group.
6
+ */
7
+ export default class FactGrouper {
8
+ private preferFirstValue;
9
+ private createFactGroup;
10
+ private createGroupKey;
11
+ /**
12
+ * Map structure { 2022_Q3: { name: ... } }. NOTE: Does not include fiscal year report key.
13
+ * All groups contain both trailing and period values, so use trailing from Q4 to get FY values.
14
+ */
15
+ buildFactGroupsByReportKey(params: {
16
+ facts: FactItem[];
17
+ splits?: SplitData[];
18
+ cik: number;
19
+ fiscalCalculator: FactFiscalCalculator;
20
+ resolvePeriodValues: boolean;
21
+ generateMissingGroups?: boolean;
22
+ }): {
23
+ factGroupsByReportKey: Map<string, FactGroup[]>;
24
+ minYear: number;
25
+ maxYear: number;
26
+ };
27
+ }
@@ -0,0 +1,206 @@
1
+ "use strict";
2
+ var __assign = (this && this.__assign) || function () {
3
+ __assign = Object.assign || function(t) {
4
+ for (var s, i = 1, n = arguments.length; i < n; i++) {
5
+ s = arguments[i];
6
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
7
+ t[p] = s[p];
8
+ }
9
+ return t;
10
+ };
11
+ return __assign.apply(this, arguments);
12
+ };
13
+ Object.defineProperty(exports, "__esModule", { value: true });
14
+ var FactPeriodResolver_1 = require("./FactPeriodResolver");
15
+ var FactSplitAdjuster_1 = require("./FactSplitAdjuster");
16
+ /**
17
+ * There are many facts properties for the same period but filed at different times.
18
+ * This groups those together and resolves the period and trailing values for each group.
19
+ */
20
+ var FactGrouper = /** @class */ (function () {
21
+ function FactGrouper() {
22
+ this.preferFirstValue = true;
23
+ }
24
+ FactGrouper.prototype.createFactGroup = function (params) {
25
+ return __assign({ name: '', unit: '', reportEnd: '', accn: '', reportFiled: '', isResolverGenerated: true, fiscalYear: 0, quarter: 0, filedFirst: '', filedLast: '', endFirst: '', endLast: '', valuePeriodFirst: null, valuePeriodLast: null, valueTrailingFirst: null, valueTrailingLast: null, valuePeriodResolved: null, valueTrailingResolved: null, valueSplitAdjustedPeriod: null, valueSplitAdjustedTrailing: null, values: [], facts: [] }, params);
26
+ };
27
+ FactGrouper.prototype.createGroupKey = function (params) {
28
+ var fiscalYear = params.fiscalYear, quarter = params.quarter, name = params.name;
29
+ return "".concat(fiscalYear, "_").concat(quarter, "_").concat(name);
30
+ };
31
+ /**
32
+ * Map structure { 2022_Q3: { name: ... } }. NOTE: Does not include fiscal year report key.
33
+ * All groups contain both trailing and period values, so use trailing from Q4 to get FY values.
34
+ */
35
+ FactGrouper.prototype.buildFactGroupsByReportKey = function (params) {
36
+ var _this = this;
37
+ var _a, _b, _c;
38
+ var facts = params.facts, cik = params.cik, fiscalCalculator = params.fiscalCalculator, resolvePeriodValues = params.resolvePeriodValues, _d = params.generateMissingGroups, generateMissingGroups = _d === void 0 ? false : _d, splits = params.splits;
39
+ // min and max year will be used to sort the reports
40
+ var minYear = 0;
41
+ var maxYear = 9999;
42
+ var factGroupByKey = new Map();
43
+ var periodResolver = new FactPeriodResolver_1.default({ cik: cik });
44
+ var factSplitAdjuster = new FactSplitAdjuster_1.default();
45
+ // used for groups that need to be generated without using a fact
46
+ var unitByPropertyName = new Map();
47
+ var accnByFiled = new Map();
48
+ // Create groups from facts.
49
+ for (var _i = 0, facts_1 = facts; _i < facts_1.length; _i++) {
50
+ var fact = facts_1[_i];
51
+ var _e = fiscalCalculator.getFiscalYearQuarter({ dateStr: fact.end }), quarter = _e.quarter, year = _e.year;
52
+ var _f = (_a = fiscalCalculator.getDatesByYearQuarter({ quarter: quarter, year: year })) !== null && _a !== void 0 ? _a : {}, _g = _f.end, reportEnd = _g === void 0 ? '' : _g, _h = _f.filed, reportFiled = _h === void 0 ? '' : _h;
53
+ var groupKey = this.createGroupKey({ fiscalYear: year, quarter: quarter, name: fact.name });
54
+ if (year < minYear)
55
+ minYear = year;
56
+ if (year > maxYear)
57
+ maxYear = year;
58
+ // period checks to see if the fact needs to be resolved for quarterly or trailing values
59
+ var period = periodResolver.getPeriod(fact);
60
+ var isPeriodFact = period <= 3;
61
+ var isTrailingFact = period > 3 || (isPeriodFact && quarter === 1);
62
+ var factValue = Number(fact.value);
63
+ // if no group exists, create from fact
64
+ if (!factGroupByKey.has(groupKey)) {
65
+ var group_1 = this.createFactGroup({
66
+ name: fact.name,
67
+ accn: periodResolver.isOriginalFiling({ end: fact.end, filed: fact.filed }) ? fact.accn : '',
68
+ unit: fact.unit,
69
+ reportEnd: reportEnd,
70
+ reportFiled: reportFiled,
71
+ isResolverGenerated: false,
72
+ fiscalYear: year,
73
+ quarter: quarter,
74
+ filedFirst: fact.filed,
75
+ filedLast: fact.filed,
76
+ endFirst: fact.end,
77
+ endLast: fact.end,
78
+ valuePeriodFirst: isPeriodFact ? factValue : null,
79
+ valuePeriodLast: isPeriodFact ? factValue : null,
80
+ valueTrailingFirst: isTrailingFact ? factValue : null,
81
+ valueTrailingLast: isTrailingFact ? factValue : null,
82
+ values: [factValue],
83
+ facts: [fact],
84
+ });
85
+ accnByFiled.set(fact.filed, (_b = fact.accn) !== null && _b !== void 0 ? _b : '');
86
+ unitByPropertyName.set(fact.name, fact.unit);
87
+ factGroupByKey.set(groupKey, group_1);
88
+ continue;
89
+ }
90
+ // if group already exists, update values
91
+ var group = factGroupByKey.get(groupKey);
92
+ group.facts.push(fact);
93
+ group.endFirst = fact.end < group.endFirst ? fact.end : group.endFirst;
94
+ group.endLast = fact.end > group.endLast ? fact.end : group.endLast;
95
+ group.filedFirst = fact.filed < group.filedFirst ? fact.filed : group.filedFirst;
96
+ group.filedLast = fact.filed > group.filedLast ? fact.filed : group.filedLast;
97
+ if (!group.values.includes(factValue)) {
98
+ group.values.push(factValue);
99
+ }
100
+ if (!group.accn && periodResolver.isOriginalFiling({ end: fact.end, filed: fact.filed })) {
101
+ group.accn = (_c = fact.accn) !== null && _c !== void 0 ? _c : '';
102
+ accnByFiled.set(fact.filed, group.accn);
103
+ }
104
+ if (isPeriodFact) {
105
+ if (group.valuePeriodFirst === null) {
106
+ group.valuePeriodFirst = factValue;
107
+ }
108
+ if (factValue !== group.valuePeriodFirst && factValue !== group.valuePeriodLast) {
109
+ group.valuePeriodLast = factValue;
110
+ }
111
+ }
112
+ if (isTrailingFact) {
113
+ if (group.valueTrailingFirst === null) {
114
+ group.valueTrailingFirst = factValue;
115
+ }
116
+ if (factValue !== group.valueTrailingFirst && factValue !== group.valueTrailingLast) {
117
+ group.valueTrailingLast = factValue;
118
+ }
119
+ }
120
+ }
121
+ // adjust for splits
122
+ var factGroupsByReportKey = new Map();
123
+ if (splits) {
124
+ factSplitAdjuster.adjustForSplits({
125
+ factGroups: Array.from(factGroupByKey.values()),
126
+ splits: splits,
127
+ });
128
+ }
129
+ factGroupByKey.forEach(function (group) {
130
+ var _a, _b;
131
+ // add to the period resolver to resolve quarterly and trailing values
132
+ var preferredValuePeriod = _this.preferFirstValue ? group.valuePeriodLast : group.valuePeriodLast;
133
+ var preferredValueTrailing = _this.preferFirstValue ? group.valueTrailingFirst : group.valueTrailingLast;
134
+ var selectedFactPeriod = group.facts.find(function (fact) {
135
+ var period = FactPeriodResolver_1.default.getPeriod(fact);
136
+ return fact.value === preferredValuePeriod && period <= 3;
137
+ });
138
+ var selectedFactTrailing = group.facts.find(function (fact) {
139
+ var period = FactPeriodResolver_1.default.getPeriod(fact);
140
+ return fact.value === preferredValueTrailing && (period > 3 || period === 0);
141
+ });
142
+ if (selectedFactPeriod) {
143
+ periodResolver.add({
144
+ end: selectedFactPeriod.end,
145
+ filed: selectedFactPeriod.filed,
146
+ name: selectedFactPeriod.name,
147
+ quarter: group.quarter,
148
+ value: Number((_a = group.valueSplitAdjustedPeriod) !== null && _a !== void 0 ? _a : preferredValuePeriod),
149
+ year: group.fiscalYear,
150
+ start: selectedFactPeriod.start,
151
+ });
152
+ }
153
+ if (selectedFactTrailing) {
154
+ periodResolver.add({
155
+ end: selectedFactTrailing.end,
156
+ filed: selectedFactTrailing.filed,
157
+ name: selectedFactTrailing.name,
158
+ quarter: group.quarter,
159
+ value: Number((_b = group.valueSplitAdjustedTrailing) !== null && _b !== void 0 ? _b : preferredValueTrailing),
160
+ year: group.fiscalYear,
161
+ start: selectedFactTrailing.start,
162
+ });
163
+ }
164
+ });
165
+ // Resolve quarterly and trailing values, and if no facts present for a certain period, create a group with resolved values.
166
+ periodResolver.forEach(function (_a) {
167
+ var _b, _c, _d, _e, _f, _g;
168
+ var propertyName = _a.propertyName, quarter = _a.quarter, valueQuarter = _a.valueQuarter, valueTrailing = _a.valueTrailing, year = _a.year;
169
+ var groupKey = _this.createGroupKey({ fiscalYear: year, name: propertyName, quarter: quarter });
170
+ var group = factGroupByKey.get(groupKey);
171
+ if (!group && (!resolvePeriodValues || !generateMissingGroups))
172
+ return;
173
+ var _h = (_b = fiscalCalculator.getDatesByYearQuarter({ quarter: quarter, year: year })) !== null && _b !== void 0 ? _b : { end: '', filed: '' }, end = _h.end, filed = _h.filed;
174
+ var reportKey = "".concat(year, "_Q").concat(quarter);
175
+ var bucket = (_c = factGroupsByReportKey.get(reportKey)) !== null && _c !== void 0 ? _c : factGroupsByReportKey.set(reportKey, []).get(reportKey);
176
+ if (!group) {
177
+ group = _this.createFactGroup({
178
+ reportEnd: end,
179
+ reportFiled: filed,
180
+ endFirst: end,
181
+ endLast: end,
182
+ name: propertyName,
183
+ accn: (_d = accnByFiled.get(filed)) !== null && _d !== void 0 ? _d : '',
184
+ unit: (_e = unitByPropertyName.get(propertyName)) !== null && _e !== void 0 ? _e : '',
185
+ isResolverGenerated: true,
186
+ fiscalYear: year,
187
+ quarter: quarter,
188
+ valuePeriodResolved: valueQuarter,
189
+ valueTrailingResolved: valueTrailing,
190
+ });
191
+ factGroupByKey.set(groupKey, group);
192
+ }
193
+ else if (resolvePeriodValues) {
194
+ var shouldUseTrailingForQuarter = FactPeriodResolver_1.default.isAverageShares({ propertyName: group.name }) && group.quarter === 4;
195
+ group.valuePeriodResolved = shouldUseTrailingForQuarter ? valueTrailing : valueQuarter;
196
+ group.valueTrailingResolved = valueTrailing;
197
+ (_f = group.valuePeriodFirst) !== null && _f !== void 0 ? _f : (group.valuePeriodFirst = shouldUseTrailingForQuarter ? valueTrailing : valueQuarter);
198
+ (_g = group.valueTrailingFirst) !== null && _g !== void 0 ? _g : (group.valueTrailingFirst = valueTrailing);
199
+ }
200
+ bucket.push(group);
201
+ });
202
+ return { factGroupsByReportKey: factGroupsByReportKey, minYear: minYear, maxYear: maxYear };
203
+ };
204
+ return FactGrouper;
205
+ }());
206
+ exports.default = FactGrouper;
@@ -17,17 +17,59 @@ export default class FactPeriodResolver {
17
17
  private readonly propertiesByYear;
18
18
  /** prevent values from being added multiple times */
19
19
  private readonly resolvedValues;
20
+ private readonly unresolvedReports;
20
21
  private readonly cik;
22
+ private readonly preferOriginalFilingValues;
21
23
  constructor(args: {
22
24
  cik: number;
25
+ preferOriginalFilingValues?: boolean;
23
26
  });
27
+ /**
28
+ * Some properties have a start and end that represent a period average, rather than a period total.
29
+ * These properties should be treated as instantaneous properties, meaning
30
+ * the value for Q4 and FY should be the same.
31
+ *
32
+ * I believe the only properties like this are share related:
33
+ * us-gaap:WeightedAverageNumberOfDilutedSharesOutstanding and us-gaap:WeightedAverageNumberOfSharesOutstandingBasic.
34
+ * May need to update this in the future if there are more
35
+ */
36
+ static isAverageShares(params: {
37
+ propertyName: string;
38
+ }): boolean;
39
+ private isAverageShares;
40
+ /**
41
+ * Used to check if this should potentially overwrite a value that has already been set.
42
+ */
43
+ private isPreferredValue;
24
44
  private getOrSetBucketArr;
25
45
  private addPropertyByYear;
26
46
  resolveValues(propertyName: string, year: number): void;
27
47
  get(propertyName: string, year: number, fiscalPeriod: FiscalPeriod): number | undefined;
28
48
  private getPropertyBuckets;
29
- private getPeriod;
49
+ /**
50
+ * 0, 3, 6, 9, or 12 month period. 0 is instantaneous data that doesn't have a time period, (ex: assets)
51
+ * other facts have values that are for a period of time. (like revenue over the last 6 months).
52
+ *
53
+ * Use periods 0 and 3 for quarterly reports and 0 and 12 for annual reports.
54
+ */
55
+ static getPeriod(params: {
56
+ start?: string;
57
+ end: string;
58
+ }): 0 | 3 | 6 | 12 | 9;
59
+ getPeriod(params: {
60
+ start?: string;
61
+ end: string;
62
+ }): 0 | 3 | 6 | 12 | 9;
30
63
  private getOrSetReport;
64
+ /**
65
+ * True if the filed date is within 60 days of the end date. This indicates that it is likely
66
+ * the original filing of the fact because filers are required to submit within 45 days of the
67
+ * period end, and subsequent reports are filed 90 days apart.
68
+ */
69
+ isOriginalFiling(params: {
70
+ end: string;
71
+ filed: string;
72
+ }): boolean;
31
73
  add(params: {
32
74
  year: number;
33
75
  quarter: number;
@@ -37,12 +79,14 @@ export default class FactPeriodResolver {
37
79
  name: string;
38
80
  filed: string;
39
81
  }): void;
40
- private readonly unresolvedReports;
41
82
  private buildUnresolvedReports;
42
83
  forEach(callback: (params: {
43
84
  year: number;
44
85
  fiscalPeriod: FiscalPeriod;
45
86
  propertyName: string;
46
87
  value: number | string;
88
+ valueQuarter: number;
89
+ valueTrailing: number;
90
+ quarter: number;
47
91
  }) => void): void;
48
92
  }
@@ -1,6 +1,5 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- var ReportRawResolvable_1 = require("./ReportRawResolvable");
4
3
  /**
5
4
  * Resolves quarterly and annual values for a property. This is because filers provide total values for the
6
5
  * current fiscal year, rather than values for the individual quarters
@@ -21,9 +20,33 @@ var FactPeriodResolver = /** @class */ (function () {
21
20
  /** prevent values from being added multiple times */
22
21
  this.resolvedValues = new Set();
23
22
  this.unresolvedReports = new Map();
24
- var cik = args.cik;
23
+ var cik = args.cik, _a = args.preferOriginalFilingValues, preferOriginalFilingValues = _a === void 0 ? false : _a;
25
24
  this.cik = cik;
25
+ this.preferOriginalFilingValues = preferOriginalFilingValues;
26
26
  }
27
+ /**
28
+ * Some properties have a start and end that represent a period average, rather than a period total.
29
+ * These properties should be treated as instantaneous properties, meaning
30
+ * the value for Q4 and FY should be the same.
31
+ *
32
+ * I believe the only properties like this are share related:
33
+ * us-gaap:WeightedAverageNumberOfDilutedSharesOutstanding and us-gaap:WeightedAverageNumberOfSharesOutstandingBasic.
34
+ * May need to update this in the future if there are more
35
+ */
36
+ FactPeriodResolver.isAverageShares = function (params) {
37
+ var propertyName = params.propertyName;
38
+ return propertyName.includes('WeightedAverage') && propertyName.includes('SharesOutstanding');
39
+ };
40
+ FactPeriodResolver.prototype.isAverageShares = function (params) {
41
+ return FactPeriodResolver.isAverageShares(params);
42
+ };
43
+ /**
44
+ * Used to check if this should potentially overwrite a value that has already been set.
45
+ */
46
+ FactPeriodResolver.prototype.isPreferredValue = function (params) {
47
+ var bucket = params.bucket, end = params.end, filed = params.filed, bucketIndex = params.bucketIndex;
48
+ return !bucket.has(bucketIndex) || this.preferOriginalFilingValues || !this.isOriginalFiling({ end: end, filed: filed });
49
+ };
27
50
  FactPeriodResolver.prototype.getOrSetBucketArr = function (map, year, propertyName) {
28
51
  var _a, _b;
29
52
  var propertyMap = (_a = map.get(year)) !== null && _a !== void 0 ? _a : map.set(year, new Map()).get(year);
@@ -49,8 +72,8 @@ var FactPeriodResolver = /** @class */ (function () {
49
72
  var reportKey = "".concat(year, "_").concat(i + 1);
50
73
  var unresolvedReport = this.unresolvedReports.get(reportKey);
51
74
  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;
75
+ var sumValue = (_b = unresolvedReport === null || unresolvedReport === void 0 ? void 0 : unresolvedReport[propertyName]) !== null && _b !== void 0 ? _b : 0;
76
+ var prevSum = (_c = unresolvedReportPrev === null || unresolvedReportPrev === void 0 ? void 0 : unresolvedReportPrev[propertyName]) !== null && _c !== void 0 ? _c : 0;
54
77
  if (quarterValue === undefined) {
55
78
  var bucketQuarter_1 = this.getOrSetBucketArr(this.valueByQuarterByPropertyByYear, year, propertyName);
56
79
  bucketQuarter_1.set(i, (Number(sumValue) || 0) - (Number(prevSum) || 0));
@@ -82,7 +105,13 @@ var FactPeriodResolver = /** @class */ (function () {
82
105
  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
106
  return { bucketQuarter: bucketQuarter, bucketSum: bucketSum, bucketString: bucketString };
84
107
  };
85
- FactPeriodResolver.prototype.getPeriod = function (params) {
108
+ /**
109
+ * 0, 3, 6, 9, or 12 month period. 0 is instantaneous data that doesn't have a time period, (ex: assets)
110
+ * other facts have values that are for a period of time. (like revenue over the last 6 months).
111
+ *
112
+ * Use periods 0 and 3 for quarterly reports and 0 and 12 for annual reports.
113
+ */
114
+ FactPeriodResolver.getPeriod = function (params) {
86
115
  var start = params.start, end = params.end;
87
116
  if (!start || start === end)
88
117
  return 0;
@@ -96,11 +125,14 @@ var FactPeriodResolver = /** @class */ (function () {
96
125
  return 9;
97
126
  return 12;
98
127
  };
128
+ FactPeriodResolver.prototype.getPeriod = function (params) {
129
+ return FactPeriodResolver.getPeriod(params);
130
+ };
99
131
  FactPeriodResolver.prototype.getOrSetReport = function (params) {
100
132
  var _a;
101
133
  var year = params.year, quarter = params.quarter;
102
134
  var key = "".concat(year, "_").concat(quarter);
103
- var report = (_a = this.unresolvedReports.get(key)) !== null && _a !== void 0 ? _a : new ReportRawResolvable_1.default({
135
+ var report = (_a = this.unresolvedReports.get(key)) !== null && _a !== void 0 ? _a : {
104
136
  cik: this.cik,
105
137
  fiscalYear: year,
106
138
  fiscalPeriod: quarter === 4 ? 'FY' : "Q".concat(quarter),
@@ -109,35 +141,54 @@ var FactPeriodResolver = /** @class */ (function () {
109
141
  url: null,
110
142
  splitDate: null,
111
143
  splitRatio: null,
112
- });
144
+ };
113
145
  if (!this.unresolvedReports.has(key)) {
114
146
  this.unresolvedReports.set(key, report);
115
147
  }
116
148
  return report;
117
149
  };
150
+ /**
151
+ * True if the filed date is within 60 days of the end date. This indicates that it is likely
152
+ * the original filing of the fact because filers are required to submit within 45 days of the
153
+ * period end, and subsequent reports are filed 90 days apart.
154
+ */
155
+ FactPeriodResolver.prototype.isOriginalFiling = function (params) {
156
+ var end = params.end, filed = params.filed;
157
+ var DAY_60_MS = 5184000000;
158
+ return new Date(filed).getTime() - new Date(end).getTime() < DAY_60_MS;
159
+ };
118
160
  FactPeriodResolver.prototype.add = function (params) {
119
161
  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 });
162
+ var bucketIndex = quarter - 1;
163
+ var period = this.isAverageShares({ propertyName: propertyName }) ? 0 : this.getPeriod({ start: start, end: end });
121
164
  this.addPropertyByYear(this.propertiesByYear, year, propertyName);
122
165
  if (typeof value === 'string') {
123
166
  var bucket = this.getOrSetBucketArr(this.valueByQuarterByPropertyByYearString, year, propertyName);
124
- bucket.set(quarter - 1, value);
167
+ if (this.isPreferredValue({ bucket: bucket, bucketIndex: bucketIndex, end: end, filed: filed })) {
168
+ bucket.set(bucketIndex, value);
169
+ }
125
170
  return;
126
171
  }
127
172
  if (period === 0) {
128
173
  var bucket = this.getOrSetBucketArr(this.valueByQuarterByPropertyByYear, year, propertyName);
129
174
  var bucketSum = this.getOrSetBucketArr(this.sumByQuarterByPropertyByYear, year, propertyName);
130
- bucket.set(quarter - 1, value);
131
- bucketSum.set(quarter - 1, value);
175
+ if (this.isPreferredValue({ bucket: bucket, bucketIndex: bucketIndex, end: end, filed: filed })) {
176
+ bucket.set(bucketIndex, value);
177
+ bucketSum.set(bucketIndex, value);
178
+ }
132
179
  return;
133
180
  }
134
181
  if (period === 3) {
135
182
  var bucket = this.getOrSetBucketArr(this.valueByQuarterByPropertyByYear, year, propertyName);
136
- bucket.set(quarter - 1, value);
183
+ if (this.isPreferredValue({ bucket: bucket, bucketIndex: bucketIndex, end: end, filed: filed })) {
184
+ bucket.set(bucketIndex, value);
185
+ }
137
186
  }
138
187
  if (quarter === 1 || period > 3) {
139
188
  var bucket = this.getOrSetBucketArr(this.sumByQuarterByPropertyByYear, year, propertyName);
140
- bucket.set(quarter - 1, value);
189
+ if (this.isPreferredValue({ bucket: bucket, bucketIndex: bucketIndex, end: end, filed: filed })) {
190
+ bucket.set(bucketIndex, value);
191
+ }
141
192
  }
142
193
  this.addPropertyByYear(this.propertiesResolvableByYear, year, propertyName);
143
194
  };
@@ -163,7 +214,7 @@ var FactPeriodResolver = /** @class */ (function () {
163
214
  if (value === undefined)
164
215
  continue;
165
216
  var report = _this.getOrSetReport({ year: year, quarter: i + 1 });
166
- report.report[propertyName] = value;
217
+ report[propertyName] = value;
167
218
  }
168
219
  });
169
220
  });
@@ -173,14 +224,24 @@ var FactPeriodResolver = /** @class */ (function () {
173
224
  this.buildUnresolvedReports();
174
225
  this.propertiesByYear.forEach(function (properties, year) {
175
226
  properties.forEach(function (propertyName) {
176
- var _a;
227
+ var _a, _b;
177
228
  _this.resolveValues(propertyName, year);
178
- for (var i = 0; i < 5; i++) {
229
+ for (var i = 0; i < 4; i++) {
179
230
  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 });
231
+ var _c = _this.getPropertyBuckets(year, propertyName), bucketQuarter = _c.bucketQuarter, bucketSum = _c.bucketSum, bucketString = _c.bucketString;
232
+ var valueQuarter = (_a = bucketQuarter.get(i)) !== null && _a !== void 0 ? _a : bucketString.get(i);
233
+ var valueTrailing = bucketSum.get(i);
234
+ var value = (_b = (isAnnual ? bucketSum.get(3) : bucketQuarter.get(i))) !== null && _b !== void 0 ? _b : bucketString.get(i);
235
+ var quarter = i + 1;
236
+ callback({
237
+ year: year,
238
+ fiscalPeriod: "Q".concat(quarter),
239
+ propertyName: propertyName,
240
+ value: value,
241
+ quarter: quarter,
242
+ valueQuarter: valueQuarter,
243
+ valueTrailing: valueTrailing,
244
+ });
184
245
  }
185
246
  });
186
247
  });
@@ -0,0 +1,9 @@
1
+ import { CompanyFactListData, FactItem } from '../../types';
2
+ /**
3
+ * Builds an array of fact records.
4
+ */
5
+ export default class FactRecordBuilder {
6
+ createFacts(data: CompanyFactListData, includeNamePrefix?: boolean): {
7
+ facts: FactItem[];
8
+ };
9
+ }