sec-edgar-api 0.2.4 → 0.3.1

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 (42) hide show
  1. package/README.md +1 -0
  2. package/build/services/DocumentParser/DocumentParser.d.ts +12 -0
  3. package/build/services/DocumentParser/DocumentParser.js +3 -0
  4. package/build/services/DocumentParser/parsers/index.d.ts +2 -0
  5. package/build/services/DocumentParser/parsers/index.js +2 -0
  6. package/build/services/DocumentParser/parsers/parse-current-filings-daily.d.ts +13 -0
  7. package/build/services/DocumentParser/parsers/parse-current-filings-daily.js +37 -0
  8. package/build/services/ReportParser/ReportParser.d.ts +10 -14
  9. package/build/services/ReportParser/ReportParser.js +17 -24
  10. package/build/services/ReportRawBuilder/FactGrouper.d.ts +27 -0
  11. package/build/services/ReportRawBuilder/FactGrouper.js +206 -0
  12. package/build/services/{ReportBuilder → ReportRawBuilder}/FactPeriodResolver.d.ts +46 -2
  13. package/build/services/{ReportBuilder → ReportRawBuilder}/FactPeriodResolver.js +82 -20
  14. package/build/services/ReportRawBuilder/FactRecordBuilder.d.ts +9 -0
  15. package/build/services/{ReportBuilder → ReportRawBuilder}/FactRecordBuilder.js +6 -24
  16. package/build/services/ReportRawBuilder/FactSplitAdjuster.d.ts +43 -0
  17. package/build/services/ReportRawBuilder/FactSplitAdjuster.js +242 -0
  18. package/build/services/ReportRawBuilder/ReportRawBuilder.d.ts +39 -0
  19. package/build/services/ReportRawBuilder/ReportRawBuilder.js +158 -0
  20. package/build/services/ReportRawBuilder/index.d.ts +2 -0
  21. package/build/services/ReportRawBuilder/index.js +4 -0
  22. package/build/services/SecEdgarApi/SecEdgarApi.d.ts +32 -5
  23. package/build/services/SecEdgarApi/SecEdgarApi.js +65 -13
  24. package/build/types/common.type.d.ts +41 -0
  25. package/build/types/company-facts.type.d.ts +29 -2
  26. package/build/types/parsed-filings.type.d.ts +1 -0
  27. package/package.json +1 -1
  28. package/build/services/ReportBuilder/FactRecordBuilder.d.ts +0 -10
  29. package/build/services/ReportBuilder/FactSplitAdjuster.d.ts +0 -50
  30. package/build/services/ReportBuilder/FactSplitAdjuster.js +0 -214
  31. package/build/services/ReportBuilder/ReportBuilder.d.ts +0 -40
  32. package/build/services/ReportBuilder/ReportBuilder.js +0 -200
  33. package/build/services/ReportBuilder/ReportRawResolvable.d.ts +0 -17
  34. package/build/services/ReportBuilder/ReportRawResolvable.js +0 -114
  35. package/build/services/ReportBuilder/index.d.ts +0 -2
  36. package/build/services/ReportBuilder/index.js +0 -4
  37. package/build/services/ReportParser/ReportRawParser.d.ts +0 -5
  38. package/build/services/ReportParser/ReportRawParser.js +0 -14
  39. package/build/util/calculation-map-by-ns.d.ts +0 -6
  40. package/build/util/calculation-map-by-ns.js +0 -9
  41. /package/build/services/{ReportBuilder → ReportRawBuilder}/FactFiscalCalculator.d.ts +0 -0
  42. /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/
@@ -13,5 +13,17 @@ export default class DocumentParser {
13
13
  parseForm13g(params: XMLParams): import("../../types").Form13GData;
14
14
  parseForm10k(params: XMLParams): import("../../types").Form10KData;
15
15
  parseFormDef14a(params: XMLParams): import("../../types").FormDef14aData;
16
+ parseCurrentFilingsDaily(params: XMLParams): {
17
+ date: string;
18
+ matchCount: number;
19
+ totalCount: number;
20
+ entries: {
21
+ accessionNumber: string;
22
+ form: string;
23
+ companyCik: number;
24
+ companyName: string;
25
+ filedDate: string;
26
+ }[];
27
+ };
16
28
  }
17
29
  export {};
@@ -20,6 +20,9 @@ var DocumentParser = /** @class */ (function () {
20
20
  DocumentParser.prototype.parseFormDef14a = function (params) {
21
21
  return this.parsersByName.parseFormDef14a(params, this.parser);
22
22
  };
23
+ DocumentParser.prototype.parseCurrentFilingsDaily = function (params) {
24
+ return this.parsersByName.parseCurrentFilingsDaily(params);
25
+ };
23
26
  return DocumentParser;
24
27
  }());
25
28
  exports.default = DocumentParser;
@@ -2,10 +2,12 @@ import { parseForm4 } from './parse-form-4';
2
2
  import { parseForm13g } from './parse-form-13g';
3
3
  import { parseForm10k } from './parse-form-10k';
4
4
  import { parseFormDef14a } from './parse-form-def14a';
5
+ import { parseCurrentFilingsDaily } from './parse-current-filings-daily';
5
6
  declare const parsers: {
6
7
  parseForm4: typeof parseForm4;
7
8
  parseForm13g: typeof parseForm13g;
8
9
  parseForm10k: typeof parseForm10k;
9
10
  parseFormDef14a: typeof parseFormDef14a;
11
+ parseCurrentFilingsDaily: typeof parseCurrentFilingsDaily;
10
12
  };
11
13
  export default parsers;
@@ -4,10 +4,12 @@ var parse_form_4_1 = require("./parse-form-4");
4
4
  var parse_form_13g_1 = require("./parse-form-13g");
5
5
  var parse_form_10k_1 = require("./parse-form-10k");
6
6
  var parse_form_def14a_1 = require("./parse-form-def14a");
7
+ var parse_current_filings_daily_1 = require("./parse-current-filings-daily");
7
8
  var parsers = {
8
9
  parseForm4: parse_form_4_1.parseForm4,
9
10
  parseForm13g: parse_form_13g_1.parseForm13g,
10
11
  parseForm10k: parse_form_10k_1.parseForm10k,
11
12
  parseFormDef14a: parse_form_def14a_1.parseFormDef14a,
13
+ parseCurrentFilingsDaily: parse_current_filings_daily_1.parseCurrentFilingsDaily,
12
14
  };
13
15
  exports.default = parsers;
@@ -0,0 +1,13 @@
1
+ import { XMLParams } from '../../../types';
2
+ export declare function parseCurrentFilingsDaily(params: XMLParams): {
3
+ date: string;
4
+ matchCount: number;
5
+ totalCount: number;
6
+ entries: {
7
+ accessionNumber: string;
8
+ form: string;
9
+ companyCik: number;
10
+ companyName: string;
11
+ filedDate: string;
12
+ }[];
13
+ };
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.parseCurrentFilingsDaily = void 0;
4
+ function parseCurrentFilingsDaily(params) {
5
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j;
6
+ var xml = params.xml;
7
+ var csv = (_a = xml.split('<hr>')[1]) !== null && _a !== void 0 ? _a : '';
8
+ var lines = csv.split('\n').slice(0, -1);
9
+ var _k = (_b = xml.match(/<strong>.{1,20}?<\/strong>/g)) !== null && _b !== void 0 ? _b : [], _l = _k[0], matchHtml = _l === void 0 ? '' : _l, _m = _k[1], totalHtml = _m === void 0 ? '' : _m;
10
+ var date = (_e = (_d = (_c = xml.split('of matches for')) === null || _c === void 0 ? void 0 : _c[1].split('is', 1)) === null || _d === void 0 ? void 0 : _d[0]) === null || _e === void 0 ? void 0 : _e.trim();
11
+ var matchCount = Number((_g = (_f = matchHtml.split('>')[1]) === null || _f === void 0 ? void 0 : _f.split('<')[0]) === null || _g === void 0 ? void 0 : _g.trim()) || 0;
12
+ var totalCount = Number((_j = (_h = totalHtml.split('>')[1]) === null || _h === void 0 ? void 0 : _h.split('<')[0]) === null || _j === void 0 ? void 0 : _j.trim()) || 0;
13
+ var entries = lines.map(function (line) {
14
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p;
15
+ var parts = line.split('<a ');
16
+ var partDate = (_a = parts[0]) !== null && _a !== void 0 ? _a : '';
17
+ var partNameCik = (_b = parts[parts.length - 1]) !== null && _b !== void 0 ? _b : '';
18
+ var partAccessionForm = (_f = (_e = (_d = ((_c = parts[1]) !== null && _c !== void 0 ? _c : '').split('/Archives/edgar/data/')[1]) === null || _d === void 0 ? void 0 : _d.split('/')) === null || _e === void 0 ? void 0 : _e[1]) !== null && _f !== void 0 ? _f : '';
19
+ var partNameCikParts = partNameCik.split('</a>');
20
+ var _q = partAccessionForm === null || partAccessionForm === void 0 ? void 0 : partAccessionForm.split('>'), accession = _q[0], formUnfiltered = _q[1];
21
+ var accessionNumber = ((_g = accession === null || accession === void 0 ? void 0 : accession.substring(0, accession.lastIndexOf('-'))) !== null && _g !== void 0 ? _g : '').trim();
22
+ var form = ((_h = formUnfiltered === null || formUnfiltered === void 0 ? void 0 : formUnfiltered.replace(/</g, '')) !== null && _h !== void 0 ? _h : '').trim();
23
+ var companyCik = Number((_l = (_k = (_j = partNameCikParts[0]) === null || _j === void 0 ? void 0 : _j.split('>')[1]) === null || _k === void 0 ? void 0 : _k.trim()) !== null && _l !== void 0 ? _l : '');
24
+ var companyName = (_o = (_m = partNameCikParts[1]) === null || _m === void 0 ? void 0 : _m.trim()) !== null && _o !== void 0 ? _o : '';
25
+ var filedDate = (_p = partDate.trim()) !== null && _p !== void 0 ? _p : '';
26
+ var _r = filedDate.split('-'), month = _r[0], day = _r[1], year = _r[2];
27
+ return {
28
+ accessionNumber: accessionNumber,
29
+ form: form,
30
+ companyCik: companyCik,
31
+ companyName: companyName,
32
+ filedDate: "".concat(year, "-").concat(month, "-").concat(day),
33
+ };
34
+ });
35
+ return { date: date, matchCount: matchCount, totalCount: totalCount, entries: entries };
36
+ }
37
+ exports.parseCurrentFilingsDaily = parseCurrentFilingsDaily;
@@ -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,23 @@ 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
- parseReportsFromRaw(reportsRaw: ReportRaw[], usePropertyResolver?: boolean): ReportWrapper[];
23
+ parseReportsFromRaw(params: {
24
+ reportsRaw: ReportRaw[];
25
+ usePropertyResolver?: boolean;
26
+ }): ReportWrapper[];
31
27
  /**
32
- * Note that this includes all reports by default, not just annual and quarterly. use options.reportsToInclude to filter
28
+ * Parse raw reports
33
29
  *
34
30
  * @see https://www.sec.gov/edgar/sec-api-documentation
35
31
  */
36
- parseReportsRaw(companyFactListData: CompanyFactListData): ReportRaw[];
32
+ parseReportsRaw(companyFactListData: CompanyFactListData, options?: Omit<GetReportsRawParams, 'symbol'>): ReportRaw[];
37
33
  /**
38
34
  * parseReportsRaw but removes meta data from the report
39
35
  */
@@ -1,37 +1,24 @@
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
  */
33
- ReportParser.prototype.parseReportsFromRaw = function (reportsRaw, usePropertyResolver) {
34
- if (usePropertyResolver === void 0) { usePropertyResolver = true; }
20
+ ReportParser.prototype.parseReportsFromRaw = function (params) {
21
+ var reportsRaw = params.reportsRaw, _a = params.usePropertyResolver, usePropertyResolver = _a === void 0 ? true : _a;
35
22
  var reportByYearQuarter = new Map();
36
23
  var reportsRawFiltered = reportsRaw;
37
24
  this.translateReportsRaw(reportsRawFiltered, function (report, reportRaw) {
@@ -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
  }