fin-ratios 0.7.1 → 1.0.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.
@@ -4,63 +4,75 @@
4
4
  var COMPANY_TICKERS_URL = "https://data.sec.gov/files/company_tickers.json";
5
5
  var EDGAR_BASE = "https://data.sec.gov/api/xbrl/companyfacts";
6
6
  var HEADERS = {
7
- "User-Agent": "fin-ratios/0.1.0 contact@fin-ratios.dev",
7
+ "User-Agent": "fin-ratios/1.0 contact@fin-ratios.dev",
8
8
  "Accept-Encoding": "gzip, deflate"
9
9
  };
10
10
  async function fetchEdgar(ticker, options = {}) {
11
- const { numYears = 3 } = options;
11
+ const { numYears = 5 } = options;
12
12
  const tickerMap = await _get(COMPANY_TICKERS_URL);
13
- const entry = Object.values(tickerMap).find((v) => v.ticker.toUpperCase() === ticker.toUpperCase());
13
+ const entry = Object.values(tickerMap).find(
14
+ (v) => v.ticker.toUpperCase() === ticker.toUpperCase()
15
+ );
14
16
  if (!entry) throw new Error(`EDGAR: ticker ${ticker} not found`);
15
17
  const cik = String(entry.cik_str).padStart(10, "0");
16
18
  const facts = await _get(`${EDGAR_BASE}/CIK${cik}.json`);
17
19
  const gaap = facts.facts["us-gaap"] ?? {};
18
20
  function _annual(concept) {
19
- const entries = gaap[concept]?.units?.USD ?? [];
20
- return entries.filter((e) => e.form === "10-K").sort((a, b) => b.end.localeCompare(a.end));
21
+ return (gaap[concept]?.units?.USD ?? []).filter((e) => e.form === "10-K").sort((a, b) => b.end.localeCompare(a.end));
21
22
  }
22
- function _latestForYear(concept, year) {
23
- const annual = _annual(concept);
24
- const match = annual.find((e) => e.end.startsWith(year));
25
- return match?.val ?? 0;
23
+ function _annualShares(concept) {
24
+ return (gaap[concept]?.units?.shares ?? []).filter((e) => e.form === "10-K").sort((a, b) => b.end.localeCompare(a.end));
26
25
  }
27
- const revenueEntries = _annual("Revenues").concat(_annual("RevenueFromContractWithCustomerExcludingAssessedTax"));
28
- const years = [...new Set(revenueEntries.map((e) => e.end.slice(0, 4)))].slice(0, numYears);
29
- const results = [];
30
- for (const year of years) {
31
- const rev = _latestForYear("Revenues", year) || _latestForYear("RevenueFromContractWithCustomerExcludingAssessedTax", year);
32
- const ni = _latestForYear("NetIncomeLoss", year);
33
- const ebit = _latestForYear("OperatingIncomeLoss", year);
34
- const ta = _latestForYear("Assets", year);
35
- const ca = _latestForYear("AssetsCurrent", year);
36
- const cash = _latestForYear("CashAndCashEquivalentsAtCarryingValue", year);
37
- const ar = _latestForYear("AccountsReceivableNetCurrent", year);
38
- const inv = _latestForYear("InventoryNet", year);
39
- const cl = _latestForYear("LiabilitiesCurrent", year);
40
- const tl = _latestForYear("Liabilities", year);
41
- const ltd = _latestForYear("LongTermDebt", year);
42
- const re = _latestForYear("RetainedEarningsAccumulatedDeficit", year);
43
- const te = _latestForYear("StockholdersEquity", year);
44
- const ocf = _latestForYear("NetCashProvidedByUsedInOperatingActivities", year);
45
- const capex = _latestForYear("PaymentsToAcquirePropertyPlantAndEquipment", year);
46
- const gp = _latestForYear("GrossProfit", year);
47
- const int = _latestForYear("InterestExpense", year);
48
- const tax = _latestForYear("IncomeTaxExpense", year) || _latestForYear("IncomeTaxesPaidNet", year);
49
- results.push({
26
+ function _pick(concept, year) {
27
+ return _annual(concept).find((e) => e.end.startsWith(year))?.val ?? 0;
28
+ }
29
+ function _pickShares(concept, year) {
30
+ return _annualShares(concept).find((e) => e.end.startsWith(year))?.val ?? 0;
31
+ }
32
+ const revEntries = _annual("Revenues").concat(
33
+ _annual("RevenueFromContractWithCustomerExcludingAssessedTax")
34
+ );
35
+ const years = [...new Set(revEntries.map((e) => e.end.slice(0, 4)))].slice(0, numYears);
36
+ return years.map((year) => {
37
+ const rev = _pick("Revenues", year) || _pick("RevenueFromContractWithCustomerExcludingAssessedTax", year);
38
+ const gp = _pick("GrossProfit", year);
39
+ const ebit = _pick("OperatingIncomeLoss", year);
40
+ const ni = _pick("NetIncomeLoss", year);
41
+ const tax = _pick("IncomeTaxExpenseBenefit", year) || _pick("IncomeTaxesPaidNet", year);
42
+ const int_ = _pick("InterestExpense", year) || _pick("InterestAndDebtExpense", year);
43
+ const depr = _pick("DepreciationDepletionAndAmortization", year) || _pick("DepreciationAndAmortization", year);
44
+ const ta = _pick("Assets", year);
45
+ const ca = _pick("AssetsCurrent", year);
46
+ const cash = _pick("CashAndCashEquivalentsAtCarryingValue", year) || _pick("CashCashEquivalentsAndShortTermInvestments", year);
47
+ const ar = _pick("AccountsReceivableNetCurrent", year);
48
+ const inv = _pick("InventoryNet", year);
49
+ const cl = _pick("LiabilitiesCurrent", year);
50
+ const tl = _pick("Liabilities", year);
51
+ const ltd = _pick("LongTermDebt", year) || _pick("LongTermDebtNoncurrent", year);
52
+ const std = _pick("ShortTermBorrowings", year) || _pick("DebtCurrent", year);
53
+ const re = _pick("RetainedEarningsAccumulatedDeficit", year);
54
+ const te = _pick("StockholdersEquity", year) || _pick("StockholdersEquityIncludingPortionAttributableToNoncontrollingInterest", year);
55
+ const ocf = _pick("NetCashProvidedByUsedInOperatingActivities", year);
56
+ const capex = _pick("PaymentsToAcquirePropertyPlantAndEquipment", year);
57
+ const divs = _pick("PaymentsOfDividends", year) || _pick("PaymentsOfDividendsCommonStock", year);
58
+ const shs = _pickShares("CommonStockSharesOutstanding", year) || _pickShares("CommonStockSharesIssued", year);
59
+ const totalDebt = ltd + std;
60
+ const ebt = ni + tax;
61
+ return {
50
62
  fiscalYear: year,
51
63
  income: {
52
64
  revenue: rev,
53
65
  grossProfit: gp,
54
66
  cogs: rev - gp,
55
67
  ebit,
56
- ebitda: 0,
68
+ ebitda: ebit + depr,
57
69
  netIncome: ni,
58
- interestExpense: Math.abs(int),
70
+ interestExpense: Math.abs(int_),
59
71
  incomeTaxExpense: tax,
60
- ebt: ni + tax,
61
- depreciationAndAmortization: 0,
62
- sharesOutstanding: 0,
63
- eps: 0
72
+ ebt,
73
+ depreciationAndAmortization: depr,
74
+ sharesOutstanding: shs,
75
+ eps: shs > 0 ? ni / shs : 0
64
76
  },
65
77
  balance: {
66
78
  totalAssets: ta,
@@ -73,56 +85,68 @@ async function fetchEdgar(ticker, options = {}) {
73
85
  retainedEarnings: re,
74
86
  totalLiabilities: tl,
75
87
  currentLiabilities: cl,
76
- accountsPayable: 0,
88
+ accountsPayable: _pick("AccountsPayableCurrent", year),
77
89
  longTermDebt: ltd,
78
- totalDebt: ltd,
90
+ totalDebt,
79
91
  totalEquity: te,
80
- sharesOutstanding: 0
92
+ sharesOutstanding: shs
81
93
  },
82
94
  cashFlow: {
83
95
  operatingCashFlow: ocf,
84
96
  capex: Math.abs(capex),
85
97
  investingCashFlow: 0,
86
- financingCashFlow: 0
98
+ financingCashFlow: 0,
99
+ dividendsPaid: Math.abs(divs)
87
100
  }
88
- });
89
- }
90
- return results;
101
+ };
102
+ });
103
+ }
104
+ function normalizeForScoring(filings) {
105
+ return [...filings].reverse().map((f) => {
106
+ const rec = {
107
+ year: f.fiscalYear,
108
+ revenue: f.income.revenue,
109
+ grossProfit: f.income.grossProfit,
110
+ ebit: f.income.ebit,
111
+ ebt: f.income.ebt,
112
+ netIncome: f.income.netIncome,
113
+ incomeTaxExpense: f.income.incomeTaxExpense,
114
+ interestExpense: f.income.interestExpense,
115
+ totalAssets: f.balance.totalAssets,
116
+ totalEquity: f.balance.totalEquity,
117
+ totalDebt: f.balance.totalDebt,
118
+ cash: f.balance.cash,
119
+ currentAssets: f.balance.currentAssets,
120
+ currentLiabilities: f.balance.currentLiabilities,
121
+ capex: f.cashFlow.capex,
122
+ depreciation: f.income.depreciationAndAmortization,
123
+ operatingCashFlow: f.cashFlow.operatingCashFlow,
124
+ accountsReceivable: f.balance.accountsReceivable
125
+ };
126
+ rec.dividendsPaid = f.cashFlow.dividendsPaid;
127
+ if (f.balance.sharesOutstanding) rec.sharesOutstanding = f.balance.sharesOutstanding;
128
+ return rec;
129
+ });
91
130
  }
92
131
  function flattenEdgarData(filings) {
93
- return [...filings].reverse().map((f) => ({
94
- year: f.fiscalYear,
95
- revenue: f.income.revenue,
96
- grossProfit: f.income.grossProfit,
97
- ebit: f.income.ebit,
98
- ebt: f.income.ebt,
99
- netIncome: f.income.netIncome,
100
- incomeTaxExpense: f.income.incomeTaxExpense,
101
- interestExpense: f.income.interestExpense,
102
- totalAssets: f.balance.totalAssets,
103
- totalEquity: f.balance.totalEquity,
104
- totalDebt: f.balance.totalDebt || f.balance.longTermDebt,
105
- cash: f.balance.cash,
106
- currentAssets: f.balance.currentAssets,
107
- currentLiabilities: f.balance.currentLiabilities,
108
- capex: f.cashFlow.capex,
109
- depreciation: f.income.depreciationAndAmortization,
110
- operatingCashFlow: f.cashFlow.operatingCashFlow,
111
- accountsReceivable: f.balance.accountsReceivable
112
- }));
132
+ return normalizeForScoring(filings);
133
+ }
134
+ async function fetchEdgarNormalized(ticker, options = {}) {
135
+ return normalizeForScoring(await fetchEdgar(ticker, options));
113
136
  }
114
137
  async function fetchEdgarFlat(ticker, options = {}) {
115
- const filings = await fetchEdgar(ticker, options);
116
- return flattenEdgarData(filings);
138
+ return fetchEdgarNormalized(ticker, options);
117
139
  }
118
140
  async function _get(url) {
119
141
  const resp = await fetch(url, { headers: HEADERS });
120
- if (!resp.ok) throw new Error(`EDGAR request failed: ${resp.status} ${url}`);
142
+ if (!resp.ok) throw new Error(`EDGAR fetch failed: ${resp.status} ${url}`);
121
143
  return resp.json();
122
144
  }
123
145
 
124
146
  exports.fetchEdgar = fetchEdgar;
125
147
  exports.fetchEdgarFlat = fetchEdgarFlat;
148
+ exports.fetchEdgarNormalized = fetchEdgarNormalized;
126
149
  exports.flattenEdgarData = flattenEdgarData;
150
+ exports.normalizeForScoring = normalizeForScoring;
127
151
  //# sourceMappingURL=index.cjs.map
128
152
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/fetchers/edgar/index.ts"],"names":[],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * SEC EDGAR fetcher for fin-ratios (TypeScript).\n *\n * Fetches financial data from the free SEC EDGAR XBRL API.\n * No API key required. US companies only.\n *\n * Rate limit: ~10 requests/second (SEC guidelines). Add delays for bulk use.\n *\n * @example\n * import { fetchEdgar } from 'fin-ratios/fetchers/edgar'\n * const filings = await fetchEdgar('AAPL', { numYears: 3 })\n * for (const f of filings) {\n * console.log(`${f.fiscalYear}: Revenue $${(f.income.revenue/1e9).toFixed(1)}B`)\n * }\n */\n\nimport type { IncomeStatement, BalanceSheet, CashFlowStatement } from '../../types/index.js'\n\nconst COMPANY_TICKERS_URL = 'https://data.sec.gov/files/company_tickers.json'\nconst EDGAR_BASE = 'https://data.sec.gov/api/xbrl/companyfacts'\n\nconst HEADERS = {\n 'User-Agent': 'fin-ratios/0.1.0 contact@fin-ratios.dev',\n 'Accept-Encoding': 'gzip, deflate',\n}\n\nexport interface EdgarOptions {\n numYears?: number\n}\n\nexport interface EdgarFilingData {\n fiscalYear: string\n income: IncomeStatement\n balance: BalanceSheet\n cashFlow: CashFlowStatement\n}\n\n/**\n * Fetch multi-year annual filings from SEC EDGAR for a US stock ticker.\n */\nexport async function fetchEdgar(\n ticker: string,\n options: EdgarOptions = {}\n): Promise<EdgarFilingData[]> {\n const { numYears = 3 } = options\n\n // Step 1: Resolve ticker → CIK\n const tickerMap = (await _get(COMPANY_TICKERS_URL)) as Record<string, { cik_str: number; ticker: string; title: string }>\n const entry = Object.values(tickerMap).find(v => v.ticker.toUpperCase() === ticker.toUpperCase())\n if (!entry) throw new Error(`EDGAR: ticker ${ticker} not found`)\n\n const cik = String(entry.cik_str).padStart(10, '0')\n\n // Step 2: Fetch company facts (XBRL)\n const facts = (await _get(`${EDGAR_BASE}/CIK${cik}.json`)) as {\n facts: {\n 'us-gaap'?: Record<string, { units: { USD?: { val: number; end: string; form: string; fp: string }[] } }>\n }\n }\n\n const gaap = facts.facts['us-gaap'] ?? {}\n\n function _annual(concept: string): { end: string; val: number }[] {\n const entries = gaap[concept]?.units?.USD ?? []\n return entries\n .filter(e => e.form === '10-K')\n .sort((a, b) => b.end.localeCompare(a.end))\n }\n\n function _latestForYear(concept: string, year: string): number {\n const annual = _annual(concept)\n const match = annual.find(e => e.end.startsWith(year))\n return match?.val ?? 0\n }\n\n // Collect available fiscal years from revenue\n const revenueEntries = _annual('Revenues').concat(_annual('RevenueFromContractWithCustomerExcludingAssessedTax'))\n const years = [...new Set(revenueEntries.map(e => e.end.slice(0, 4)))].slice(0, numYears)\n\n const results: EdgarFilingData[] = []\n\n for (const year of years) {\n const rev = _latestForYear('Revenues', year) || _latestForYear('RevenueFromContractWithCustomerExcludingAssessedTax', year)\n const ni = _latestForYear('NetIncomeLoss', year)\n const ebit = _latestForYear('OperatingIncomeLoss', year)\n const ta = _latestForYear('Assets', year)\n const ca = _latestForYear('AssetsCurrent', year)\n const cash = _latestForYear('CashAndCashEquivalentsAtCarryingValue', year)\n const ar = _latestForYear('AccountsReceivableNetCurrent', year)\n const inv = _latestForYear('InventoryNet', year)\n const cl = _latestForYear('LiabilitiesCurrent', year)\n const tl = _latestForYear('Liabilities', year)\n const ltd = _latestForYear('LongTermDebt', year)\n const re = _latestForYear('RetainedEarningsAccumulatedDeficit', year)\n const te = _latestForYear('StockholdersEquity', year)\n const ocf = _latestForYear('NetCashProvidedByUsedInOperatingActivities', year)\n const capex = _latestForYear('PaymentsToAcquirePropertyPlantAndEquipment', year)\n const gp = _latestForYear('GrossProfit', year)\n const int = _latestForYear('InterestExpense', year)\n const tax = _latestForYear('IncomeTaxExpense', year) || _latestForYear('IncomeTaxesPaidNet', year)\n\n results.push({\n fiscalYear: year,\n income: {\n revenue: rev, grossProfit: gp, cogs: rev - gp,\n ebit, ebitda: 0, netIncome: ni,\n interestExpense: Math.abs(int), incomeTaxExpense: tax, ebt: ni + tax,\n depreciationAndAmortization: 0, sharesOutstanding: 0, eps: 0,\n },\n balance: {\n totalAssets: ta, currentAssets: ca, cash, accountsReceivable: ar,\n inventory: inv, netPPE: 0, goodwill: 0,\n retainedEarnings: re, totalLiabilities: tl, currentLiabilities: cl,\n accountsPayable: 0, longTermDebt: ltd, totalDebt: ltd,\n totalEquity: te, sharesOutstanding: 0,\n },\n cashFlow: {\n operatingCashFlow: ocf, capex: Math.abs(capex),\n investingCashFlow: 0, financingCashFlow: 0,\n },\n })\n }\n\n return results\n}\n\n/**\n * Flatten EDGAR filing data into the format expected by scoring utilities\n * (moatScore, capitalAllocationScore, earningsQualityScore, qualityScore).\n * Returns records in chronological order (oldest first).\n */\nexport function flattenEdgarData(filings: EdgarFilingData[]): Array<{\n year: string\n revenue: number\n grossProfit: number\n ebit: number\n ebt: number\n netIncome: number\n incomeTaxExpense: number\n interestExpense: number\n totalAssets: number\n totalEquity: number\n totalDebt: number\n cash: number\n currentAssets: number\n currentLiabilities: number\n capex: number\n depreciation: number\n operatingCashFlow: number\n accountsReceivable: number\n}> {\n return [...filings]\n .reverse() // EDGAR returns newest-first; we want oldest-first\n .map(f => ({\n year: f.fiscalYear,\n revenue: f.income.revenue,\n grossProfit: f.income.grossProfit,\n ebit: f.income.ebit,\n ebt: f.income.ebt,\n netIncome: f.income.netIncome,\n incomeTaxExpense: f.income.incomeTaxExpense,\n interestExpense: f.income.interestExpense,\n totalAssets: f.balance.totalAssets,\n totalEquity: f.balance.totalEquity,\n totalDebt: f.balance.totalDebt || f.balance.longTermDebt,\n cash: f.balance.cash,\n currentAssets: f.balance.currentAssets,\n currentLiabilities: f.balance.currentLiabilities,\n capex: f.cashFlow.capex,\n depreciation: f.income.depreciationAndAmortization,\n operatingCashFlow: f.cashFlow.operatingCashFlow,\n accountsReceivable: f.balance.accountsReceivable,\n }))\n}\n\n/**\n * Fetch EDGAR data for a ticker and return flat records ready for scoring utilities.\n * Records are returned oldest-first.\n */\nexport async function fetchEdgarFlat(\n ticker: string,\n options: EdgarOptions = {},\n): Promise<ReturnType<typeof flattenEdgarData>> {\n const filings = await fetchEdgar(ticker, options)\n return flattenEdgarData(filings)\n}\n\nasync function _get(url: string): Promise<unknown> {\n const resp = await fetch(url, { headers: HEADERS })\n if (!resp.ok) throw new Error(`EDGAR request failed: ${resp.status} ${url}`)\n return resp.json()\n}\n"]}
1
+ {"version":3,"sources":["../../../src/fetchers/edgar/index.ts"],"names":[],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * SEC EDGAR fetcher for fin-ratios (TypeScript).\n *\n * Fetches financial data from the free SEC EDGAR XBRL API.\n * No API key required. US companies only.\n *\n * Rate limit: ~10 requests/second (SEC guidelines). Add delays for bulk use.\n *\n * @example\n * import { fetchEdgar, fetchEdgarNormalized } from 'fin-ratios/fetchers/edgar'\n *\n * // Structured filings (newest-first)\n * const filings = await fetchEdgar('AAPL', { numYears: 5 })\n *\n * // Flat records ready for scoring utilities (oldest-first)\n * const annualData = await fetchEdgarNormalized('AAPL', { numYears: 10 })\n * const quality = qualityScore(annualData)\n */\n\nimport type { IncomeStatement, BalanceSheet, CashFlowStatement } from '../../types/index.js'\nimport type { AnnualQualityData } from '../../utils/quality-score.js'\nimport type { AnnualCapitalData } from '../../utils/capital-allocation.js'\n\nconst COMPANY_TICKERS_URL = 'https://data.sec.gov/files/company_tickers.json'\nconst EDGAR_BASE = 'https://data.sec.gov/api/xbrl/companyfacts'\n\nconst HEADERS = {\n 'User-Agent': 'fin-ratios/1.0 contact@fin-ratios.dev',\n 'Accept-Encoding': 'gzip, deflate',\n}\n\nexport interface EdgarOptions {\n numYears?: number\n}\n\nexport interface EdgarFilingData {\n fiscalYear: string\n income: IncomeStatement\n balance: BalanceSheet\n cashFlow: CashFlowStatement & { dividendsPaid: number }\n}\n\n/**\n * Normalised annual record that satisfies all scoring utility input types:\n * moatScore, capitalAllocationScore, earningsQualityScore, qualityScore.\n */\nexport type EdgarAnnualRecord = AnnualQualityData &\n AnnualCapitalData & {\n year: string\n sharesOutstanding?: number\n currentAssets?: number\n currentLiabilities?: number\n }\n\n/**\n * Fetch multi-year annual filings from SEC EDGAR for a US stock ticker.\n * Returns newest-first.\n */\nexport async function fetchEdgar(\n ticker: string,\n options: EdgarOptions = {}\n): Promise<EdgarFilingData[]> {\n const { numYears = 5 } = options\n\n // Resolve ticker → CIK\n const tickerMap = (await _get(COMPANY_TICKERS_URL)) as Record<\n string,\n { cik_str: number; ticker: string; title: string }\n >\n const entry = Object.values(tickerMap).find(\n v => v.ticker.toUpperCase() === ticker.toUpperCase()\n )\n if (!entry) throw new Error(`EDGAR: ticker ${ticker} not found`)\n\n const cik = String(entry.cik_str).padStart(10, '0')\n const facts = (await _get(`${EDGAR_BASE}/CIK${cik}.json`)) as {\n facts: {\n 'us-gaap'?: Record<\n string,\n {\n units: {\n USD?: { val: number; end: string; form: string; fp: string }[]\n shares?: { val: number; end: string; form: string; fp: string }[]\n }\n }\n >\n }\n }\n\n const gaap = facts.facts['us-gaap'] ?? {}\n\n function _annual(concept: string): { end: string; val: number }[] {\n return (gaap[concept]?.units?.USD ?? [])\n .filter(e => e.form === '10-K')\n .sort((a, b) => b.end.localeCompare(a.end))\n }\n\n function _annualShares(concept: string): { end: string; val: number }[] {\n return (gaap[concept]?.units?.shares ?? [])\n .filter(e => e.form === '10-K')\n .sort((a, b) => b.end.localeCompare(a.end))\n }\n\n function _pick(concept: string, year: string): number {\n return _annual(concept).find(e => e.end.startsWith(year))?.val ?? 0\n }\n\n function _pickShares(concept: string, year: string): number {\n return _annualShares(concept).find(e => e.end.startsWith(year))?.val ?? 0\n }\n\n // Determine available fiscal years from revenue\n const revEntries = _annual('Revenues').concat(\n _annual('RevenueFromContractWithCustomerExcludingAssessedTax')\n )\n const years = [...new Set(revEntries.map(e => e.end.slice(0, 4)))].slice(0, numYears)\n\n return years.map(year => {\n const rev = _pick('Revenues', year) || _pick('RevenueFromContractWithCustomerExcludingAssessedTax', year)\n const gp = _pick('GrossProfit', year)\n const ebit = _pick('OperatingIncomeLoss', year)\n const ni = _pick('NetIncomeLoss', year)\n const tax = _pick('IncomeTaxExpenseBenefit', year) || _pick('IncomeTaxesPaidNet', year)\n const int_ = _pick('InterestExpense', year) || _pick('InterestAndDebtExpense', year)\n const depr = _pick('DepreciationDepletionAndAmortization', year) || _pick('DepreciationAndAmortization', year)\n const ta = _pick('Assets', year)\n const ca = _pick('AssetsCurrent', year)\n const cash = _pick('CashAndCashEquivalentsAtCarryingValue', year) || _pick('CashCashEquivalentsAndShortTermInvestments', year)\n const ar = _pick('AccountsReceivableNetCurrent', year)\n const inv = _pick('InventoryNet', year)\n const cl = _pick('LiabilitiesCurrent', year)\n const tl = _pick('Liabilities', year)\n const ltd = _pick('LongTermDebt', year) || _pick('LongTermDebtNoncurrent', year)\n const std = _pick('ShortTermBorrowings', year) || _pick('DebtCurrent', year)\n const re = _pick('RetainedEarningsAccumulatedDeficit', year)\n const te = _pick('StockholdersEquity', year) || _pick('StockholdersEquityIncludingPortionAttributableToNoncontrollingInterest', year)\n const ocf = _pick('NetCashProvidedByUsedInOperatingActivities', year)\n const capex = _pick('PaymentsToAcquirePropertyPlantAndEquipment', year)\n const divs = _pick('PaymentsOfDividends', year) || _pick('PaymentsOfDividendsCommonStock', year)\n const shs = _pickShares('CommonStockSharesOutstanding', year) || _pickShares('CommonStockSharesIssued', year)\n\n const totalDebt = ltd + std\n const ebt = ni + tax\n\n return {\n fiscalYear: year,\n income: {\n revenue: rev, grossProfit: gp, cogs: rev - gp,\n ebit, ebitda: ebit + depr, netIncome: ni,\n interestExpense: Math.abs(int_), incomeTaxExpense: tax, ebt,\n depreciationAndAmortization: depr,\n sharesOutstanding: shs, eps: shs > 0 ? ni / shs : 0,\n },\n balance: {\n totalAssets: ta, currentAssets: ca, cash, accountsReceivable: ar,\n inventory: inv, netPPE: 0, goodwill: 0,\n retainedEarnings: re, totalLiabilities: tl, currentLiabilities: cl,\n accountsPayable: _pick('AccountsPayableCurrent', year),\n longTermDebt: ltd, totalDebt,\n totalEquity: te, sharesOutstanding: shs,\n },\n cashFlow: {\n operatingCashFlow: ocf, capex: Math.abs(capex),\n investingCashFlow: 0, financingCashFlow: 0,\n dividendsPaid: Math.abs(divs),\n },\n }\n })\n}\n\n/**\n * Flatten EDGAR filings into a list of normalised annual records, oldest-first.\n * The returned type satisfies all scoring utility inputs:\n * moatScore, capitalAllocationScore, earningsQualityScore, qualityScore.\n */\nexport function normalizeForScoring(filings: EdgarFilingData[]): EdgarAnnualRecord[] {\n return [...filings].reverse().map(f => {\n const rec: EdgarAnnualRecord = {\n year: f.fiscalYear,\n revenue: f.income.revenue,\n grossProfit: f.income.grossProfit,\n ebit: f.income.ebit,\n ebt: f.income.ebt,\n netIncome: f.income.netIncome,\n incomeTaxExpense: f.income.incomeTaxExpense,\n interestExpense: f.income.interestExpense,\n totalAssets: f.balance.totalAssets,\n totalEquity: f.balance.totalEquity,\n totalDebt: f.balance.totalDebt,\n cash: f.balance.cash,\n currentAssets: f.balance.currentAssets,\n currentLiabilities: f.balance.currentLiabilities,\n capex: f.cashFlow.capex,\n depreciation: f.income.depreciationAndAmortization,\n operatingCashFlow: f.cashFlow.operatingCashFlow,\n accountsReceivable: f.balance.accountsReceivable,\n }\n rec.dividendsPaid = f.cashFlow.dividendsPaid\n if (f.balance.sharesOutstanding) rec.sharesOutstanding = f.balance.sharesOutstanding\n return rec\n })\n}\n\n/**\n * @deprecated Use `normalizeForScoring` instead.\n * Kept for backwards compatibility.\n */\nexport function flattenEdgarData(filings: EdgarFilingData[]): EdgarAnnualRecord[] {\n return normalizeForScoring(filings)\n}\n\n/**\n * Fetch EDGAR data and return normalised annual records (oldest-first),\n * ready to pass directly to qualityScore(), moatScore(), etc.\n *\n * @example\n * const annualData = await fetchEdgarNormalized('AAPL', { numYears: 10 })\n * const qs = qualityScore(annualData)\n */\nexport async function fetchEdgarNormalized(\n ticker: string,\n options: EdgarOptions = {},\n): Promise<EdgarAnnualRecord[]> {\n return normalizeForScoring(await fetchEdgar(ticker, options))\n}\n\n/**\n * @deprecated Use `fetchEdgarNormalized` instead.\n */\nexport async function fetchEdgarFlat(\n ticker: string,\n options: EdgarOptions = {},\n): Promise<EdgarAnnualRecord[]> {\n return fetchEdgarNormalized(ticker, options)\n}\n\nasync function _get(url: string): Promise<unknown> {\n const resp = await fetch(url, { headers: HEADERS })\n if (!resp.ok) throw new Error(`EDGAR fetch failed: ${resp.status} ${url}`)\n return resp.json()\n}\n"]}
@@ -1,4 +1,5 @@
1
1
  import { I as IncomeStatement, B as BalanceSheet, C as CashFlowStatement } from '../../financials-DUHxWDPS.cjs';
2
+ import { A as AnnualQualityData, a as AnnualCapitalData } from '../../quality-score-DC4evvdz.cjs';
2
3
 
3
4
  /**
4
5
  * SEC EDGAR fetcher for fin-ratios (TypeScript).
@@ -9,11 +10,14 @@ import { I as IncomeStatement, B as BalanceSheet, C as CashFlowStatement } from
9
10
  * Rate limit: ~10 requests/second (SEC guidelines). Add delays for bulk use.
10
11
  *
11
12
  * @example
12
- * import { fetchEdgar } from 'fin-ratios/fetchers/edgar'
13
- * const filings = await fetchEdgar('AAPL', { numYears: 3 })
14
- * for (const f of filings) {
15
- * console.log(`${f.fiscalYear}: Revenue $${(f.income.revenue/1e9).toFixed(1)}B`)
16
- * }
13
+ * import { fetchEdgar, fetchEdgarNormalized } from 'fin-ratios/fetchers/edgar'
14
+ *
15
+ * // Structured filings (newest-first)
16
+ * const filings = await fetchEdgar('AAPL', { numYears: 5 })
17
+ *
18
+ * // Flat records ready for scoring utilities (oldest-first)
19
+ * const annualData = await fetchEdgarNormalized('AAPL', { numYears: 10 })
20
+ * const quality = qualityScore(annualData)
17
21
  */
18
22
 
19
23
  interface EdgarOptions {
@@ -23,41 +27,48 @@ interface EdgarFilingData {
23
27
  fiscalYear: string;
24
28
  income: IncomeStatement;
25
29
  balance: BalanceSheet;
26
- cashFlow: CashFlowStatement;
30
+ cashFlow: CashFlowStatement & {
31
+ dividendsPaid: number;
32
+ };
27
33
  }
34
+ /**
35
+ * Normalised annual record that satisfies all scoring utility input types:
36
+ * moatScore, capitalAllocationScore, earningsQualityScore, qualityScore.
37
+ */
38
+ type EdgarAnnualRecord = AnnualQualityData & AnnualCapitalData & {
39
+ year: string;
40
+ sharesOutstanding?: number;
41
+ currentAssets?: number;
42
+ currentLiabilities?: number;
43
+ };
28
44
  /**
29
45
  * Fetch multi-year annual filings from SEC EDGAR for a US stock ticker.
46
+ * Returns newest-first.
30
47
  */
31
48
  declare function fetchEdgar(ticker: string, options?: EdgarOptions): Promise<EdgarFilingData[]>;
32
49
  /**
33
- * Flatten EDGAR filing data into the format expected by scoring utilities
34
- * (moatScore, capitalAllocationScore, earningsQualityScore, qualityScore).
35
- * Returns records in chronological order (oldest first).
50
+ * Flatten EDGAR filings into a list of normalised annual records, oldest-first.
51
+ * The returned type satisfies all scoring utility inputs:
52
+ * moatScore, capitalAllocationScore, earningsQualityScore, qualityScore.
36
53
  */
37
- declare function flattenEdgarData(filings: EdgarFilingData[]): Array<{
38
- year: string;
39
- revenue: number;
40
- grossProfit: number;
41
- ebit: number;
42
- ebt: number;
43
- netIncome: number;
44
- incomeTaxExpense: number;
45
- interestExpense: number;
46
- totalAssets: number;
47
- totalEquity: number;
48
- totalDebt: number;
49
- cash: number;
50
- currentAssets: number;
51
- currentLiabilities: number;
52
- capex: number;
53
- depreciation: number;
54
- operatingCashFlow: number;
55
- accountsReceivable: number;
56
- }>;
54
+ declare function normalizeForScoring(filings: EdgarFilingData[]): EdgarAnnualRecord[];
55
+ /**
56
+ * @deprecated Use `normalizeForScoring` instead.
57
+ * Kept for backwards compatibility.
58
+ */
59
+ declare function flattenEdgarData(filings: EdgarFilingData[]): EdgarAnnualRecord[];
60
+ /**
61
+ * Fetch EDGAR data and return normalised annual records (oldest-first),
62
+ * ready to pass directly to qualityScore(), moatScore(), etc.
63
+ *
64
+ * @example
65
+ * const annualData = await fetchEdgarNormalized('AAPL', { numYears: 10 })
66
+ * const qs = qualityScore(annualData)
67
+ */
68
+ declare function fetchEdgarNormalized(ticker: string, options?: EdgarOptions): Promise<EdgarAnnualRecord[]>;
57
69
  /**
58
- * Fetch EDGAR data for a ticker and return flat records ready for scoring utilities.
59
- * Records are returned oldest-first.
70
+ * @deprecated Use `fetchEdgarNormalized` instead.
60
71
  */
61
- declare function fetchEdgarFlat(ticker: string, options?: EdgarOptions): Promise<ReturnType<typeof flattenEdgarData>>;
72
+ declare function fetchEdgarFlat(ticker: string, options?: EdgarOptions): Promise<EdgarAnnualRecord[]>;
62
73
 
63
- export { type EdgarFilingData, type EdgarOptions, fetchEdgar, fetchEdgarFlat, flattenEdgarData };
74
+ export { type EdgarAnnualRecord, type EdgarFilingData, type EdgarOptions, fetchEdgar, fetchEdgarFlat, fetchEdgarNormalized, flattenEdgarData, normalizeForScoring };
@@ -1,4 +1,5 @@
1
1
  import { I as IncomeStatement, B as BalanceSheet, C as CashFlowStatement } from '../../financials-DUHxWDPS.js';
2
+ import { A as AnnualQualityData, a as AnnualCapitalData } from '../../quality-score-DC4evvdz.js';
2
3
 
3
4
  /**
4
5
  * SEC EDGAR fetcher for fin-ratios (TypeScript).
@@ -9,11 +10,14 @@ import { I as IncomeStatement, B as BalanceSheet, C as CashFlowStatement } from
9
10
  * Rate limit: ~10 requests/second (SEC guidelines). Add delays for bulk use.
10
11
  *
11
12
  * @example
12
- * import { fetchEdgar } from 'fin-ratios/fetchers/edgar'
13
- * const filings = await fetchEdgar('AAPL', { numYears: 3 })
14
- * for (const f of filings) {
15
- * console.log(`${f.fiscalYear}: Revenue $${(f.income.revenue/1e9).toFixed(1)}B`)
16
- * }
13
+ * import { fetchEdgar, fetchEdgarNormalized } from 'fin-ratios/fetchers/edgar'
14
+ *
15
+ * // Structured filings (newest-first)
16
+ * const filings = await fetchEdgar('AAPL', { numYears: 5 })
17
+ *
18
+ * // Flat records ready for scoring utilities (oldest-first)
19
+ * const annualData = await fetchEdgarNormalized('AAPL', { numYears: 10 })
20
+ * const quality = qualityScore(annualData)
17
21
  */
18
22
 
19
23
  interface EdgarOptions {
@@ -23,41 +27,48 @@ interface EdgarFilingData {
23
27
  fiscalYear: string;
24
28
  income: IncomeStatement;
25
29
  balance: BalanceSheet;
26
- cashFlow: CashFlowStatement;
30
+ cashFlow: CashFlowStatement & {
31
+ dividendsPaid: number;
32
+ };
27
33
  }
34
+ /**
35
+ * Normalised annual record that satisfies all scoring utility input types:
36
+ * moatScore, capitalAllocationScore, earningsQualityScore, qualityScore.
37
+ */
38
+ type EdgarAnnualRecord = AnnualQualityData & AnnualCapitalData & {
39
+ year: string;
40
+ sharesOutstanding?: number;
41
+ currentAssets?: number;
42
+ currentLiabilities?: number;
43
+ };
28
44
  /**
29
45
  * Fetch multi-year annual filings from SEC EDGAR for a US stock ticker.
46
+ * Returns newest-first.
30
47
  */
31
48
  declare function fetchEdgar(ticker: string, options?: EdgarOptions): Promise<EdgarFilingData[]>;
32
49
  /**
33
- * Flatten EDGAR filing data into the format expected by scoring utilities
34
- * (moatScore, capitalAllocationScore, earningsQualityScore, qualityScore).
35
- * Returns records in chronological order (oldest first).
50
+ * Flatten EDGAR filings into a list of normalised annual records, oldest-first.
51
+ * The returned type satisfies all scoring utility inputs:
52
+ * moatScore, capitalAllocationScore, earningsQualityScore, qualityScore.
36
53
  */
37
- declare function flattenEdgarData(filings: EdgarFilingData[]): Array<{
38
- year: string;
39
- revenue: number;
40
- grossProfit: number;
41
- ebit: number;
42
- ebt: number;
43
- netIncome: number;
44
- incomeTaxExpense: number;
45
- interestExpense: number;
46
- totalAssets: number;
47
- totalEquity: number;
48
- totalDebt: number;
49
- cash: number;
50
- currentAssets: number;
51
- currentLiabilities: number;
52
- capex: number;
53
- depreciation: number;
54
- operatingCashFlow: number;
55
- accountsReceivable: number;
56
- }>;
54
+ declare function normalizeForScoring(filings: EdgarFilingData[]): EdgarAnnualRecord[];
55
+ /**
56
+ * @deprecated Use `normalizeForScoring` instead.
57
+ * Kept for backwards compatibility.
58
+ */
59
+ declare function flattenEdgarData(filings: EdgarFilingData[]): EdgarAnnualRecord[];
60
+ /**
61
+ * Fetch EDGAR data and return normalised annual records (oldest-first),
62
+ * ready to pass directly to qualityScore(), moatScore(), etc.
63
+ *
64
+ * @example
65
+ * const annualData = await fetchEdgarNormalized('AAPL', { numYears: 10 })
66
+ * const qs = qualityScore(annualData)
67
+ */
68
+ declare function fetchEdgarNormalized(ticker: string, options?: EdgarOptions): Promise<EdgarAnnualRecord[]>;
57
69
  /**
58
- * Fetch EDGAR data for a ticker and return flat records ready for scoring utilities.
59
- * Records are returned oldest-first.
70
+ * @deprecated Use `fetchEdgarNormalized` instead.
60
71
  */
61
- declare function fetchEdgarFlat(ticker: string, options?: EdgarOptions): Promise<ReturnType<typeof flattenEdgarData>>;
72
+ declare function fetchEdgarFlat(ticker: string, options?: EdgarOptions): Promise<EdgarAnnualRecord[]>;
62
73
 
63
- export { type EdgarFilingData, type EdgarOptions, fetchEdgar, fetchEdgarFlat, flattenEdgarData };
74
+ export { type EdgarAnnualRecord, type EdgarFilingData, type EdgarOptions, fetchEdgar, fetchEdgarFlat, fetchEdgarNormalized, flattenEdgarData, normalizeForScoring };