fin-ratios 0.4.0 → 0.7.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.
@@ -89,6 +89,32 @@ async function fetchEdgar(ticker, options = {}) {
89
89
  }
90
90
  return results;
91
91
  }
92
+ 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
+ }));
113
+ }
114
+ async function fetchEdgarFlat(ticker, options = {}) {
115
+ const filings = await fetchEdgar(ticker, options);
116
+ return flattenEdgarData(filings);
117
+ }
92
118
  async function _get(url) {
93
119
  const resp = await fetch(url, { headers: HEADERS });
94
120
  if (!resp.ok) throw new Error(`EDGAR request failed: ${resp.status} ${url}`);
@@ -96,5 +122,7 @@ async function _get(url) {
96
122
  }
97
123
 
98
124
  exports.fetchEdgar = fetchEdgar;
125
+ exports.fetchEdgarFlat = fetchEdgarFlat;
126
+ exports.flattenEdgarData = flattenEdgarData;
99
127
  //# sourceMappingURL=index.cjs.map
100
128
  //# 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\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 } 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"]}
@@ -29,5 +29,35 @@ interface EdgarFilingData {
29
29
  * Fetch multi-year annual filings from SEC EDGAR for a US stock ticker.
30
30
  */
31
31
  declare function fetchEdgar(ticker: string, options?: EdgarOptions): Promise<EdgarFilingData[]>;
32
+ /**
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).
36
+ */
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
+ }>;
57
+ /**
58
+ * Fetch EDGAR data for a ticker and return flat records ready for scoring utilities.
59
+ * Records are returned oldest-first.
60
+ */
61
+ declare function fetchEdgarFlat(ticker: string, options?: EdgarOptions): Promise<ReturnType<typeof flattenEdgarData>>;
32
62
 
33
- export { type EdgarFilingData, type EdgarOptions, fetchEdgar };
63
+ export { type EdgarFilingData, type EdgarOptions, fetchEdgar, fetchEdgarFlat, flattenEdgarData };
@@ -29,5 +29,35 @@ interface EdgarFilingData {
29
29
  * Fetch multi-year annual filings from SEC EDGAR for a US stock ticker.
30
30
  */
31
31
  declare function fetchEdgar(ticker: string, options?: EdgarOptions): Promise<EdgarFilingData[]>;
32
+ /**
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).
36
+ */
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
+ }>;
57
+ /**
58
+ * Fetch EDGAR data for a ticker and return flat records ready for scoring utilities.
59
+ * Records are returned oldest-first.
60
+ */
61
+ declare function fetchEdgarFlat(ticker: string, options?: EdgarOptions): Promise<ReturnType<typeof flattenEdgarData>>;
32
62
 
33
- export { type EdgarFilingData, type EdgarOptions, fetchEdgar };
63
+ export { type EdgarFilingData, type EdgarOptions, fetchEdgar, fetchEdgarFlat, flattenEdgarData };
@@ -87,12 +87,38 @@ async function fetchEdgar(ticker, options = {}) {
87
87
  }
88
88
  return results;
89
89
  }
90
+ function flattenEdgarData(filings) {
91
+ return [...filings].reverse().map((f) => ({
92
+ year: f.fiscalYear,
93
+ revenue: f.income.revenue,
94
+ grossProfit: f.income.grossProfit,
95
+ ebit: f.income.ebit,
96
+ ebt: f.income.ebt,
97
+ netIncome: f.income.netIncome,
98
+ incomeTaxExpense: f.income.incomeTaxExpense,
99
+ interestExpense: f.income.interestExpense,
100
+ totalAssets: f.balance.totalAssets,
101
+ totalEquity: f.balance.totalEquity,
102
+ totalDebt: f.balance.totalDebt || f.balance.longTermDebt,
103
+ cash: f.balance.cash,
104
+ currentAssets: f.balance.currentAssets,
105
+ currentLiabilities: f.balance.currentLiabilities,
106
+ capex: f.cashFlow.capex,
107
+ depreciation: f.income.depreciationAndAmortization,
108
+ operatingCashFlow: f.cashFlow.operatingCashFlow,
109
+ accountsReceivable: f.balance.accountsReceivable
110
+ }));
111
+ }
112
+ async function fetchEdgarFlat(ticker, options = {}) {
113
+ const filings = await fetchEdgar(ticker, options);
114
+ return flattenEdgarData(filings);
115
+ }
90
116
  async function _get(url) {
91
117
  const resp = await fetch(url, { headers: HEADERS });
92
118
  if (!resp.ok) throw new Error(`EDGAR request failed: ${resp.status} ${url}`);
93
119
  return resp.json();
94
120
  }
95
121
 
96
- export { fetchEdgar };
122
+ export { fetchEdgar, fetchEdgarFlat, flattenEdgarData };
97
123
  //# sourceMappingURL=index.js.map
98
124
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/fetchers/edgar/index.ts"],"names":[],"mappings":"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","file":"index.js","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\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.js","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"]}
package/dist/index.cjs CHANGED
@@ -119,10 +119,10 @@ gordonGrowthModel.formula = "D1 / (r - g)";
119
119
  gordonGrowthModel.description = "DDM for stable dividend-paying stocks. Only valid when r > g.";
120
120
  function reverseDcf(input) {
121
121
  const target = input.marketCap + (input.netDebt ?? 0);
122
- const computeEV = (g4) => {
122
+ const computeEV = (g5) => {
123
123
  const result = dcf2Stage({
124
124
  baseFcf: input.baseFcf,
125
- growthRate: g4,
125
+ growthRate: g5,
126
126
  years: input.years,
127
127
  terminalGrowthRate: input.terminalGrowthRate,
128
128
  wacc: input.wacc
@@ -452,8 +452,8 @@ function annualizeReturn(returnValue, periodsPerYear) {
452
452
  }
453
453
  function stdDev(values, ddof = 1) {
454
454
  if (values.length < 2) return null;
455
- const mean4 = values.reduce((a, b) => a + b, 0) / values.length;
456
- const variance = values.reduce((sum, v) => sum + Math.pow(v - mean4, 2), 0) / (values.length - ddof);
455
+ const mean5 = values.reduce((a, b) => a + b, 0) / values.length;
456
+ const variance = values.reduce((sum, v) => sum + Math.pow(v - mean5, 2), 0) / (values.length - ddof);
457
457
  return Math.sqrt(variance);
458
458
  }
459
459
  function mean(values) {
@@ -1725,6 +1725,340 @@ function capitalAllocationScore(annualData, options = {}) {
1725
1725
  };
1726
1726
  }
1727
1727
 
1728
+ // src/utils/earnings-quality.ts
1729
+ function mean4(xs) {
1730
+ return xs.length ? xs.reduce((a, b) => a + b, 0) / xs.length : 0;
1731
+ }
1732
+ function std3(xs) {
1733
+ if (xs.length < 2) return 0;
1734
+ const m = mean4(xs);
1735
+ return Math.sqrt(xs.reduce((a, x) => a + (x - m) ** 2, 0) / (xs.length - 1));
1736
+ }
1737
+ function cv3(xs) {
1738
+ const m = mean4(xs);
1739
+ return Math.abs(m) > 1e-9 ? std3(xs) / Math.abs(m) : 1;
1740
+ }
1741
+ function olsSlope3(ys) {
1742
+ const n = ys.length;
1743
+ if (n < 2) return 0;
1744
+ const xm = (n - 1) / 2;
1745
+ const ym = mean4(ys);
1746
+ const ssXX = Array.from({ length: n }, (_, i) => (i - xm) ** 2).reduce((a, b) => a + b, 0);
1747
+ const ssXY = Array.from({ length: n }, (_, i) => (i - xm) * ((ys[i] ?? 0) - ym)).reduce((a, b) => a + b, 0);
1748
+ return ssXX ? ssXY / ssXX : 0;
1749
+ }
1750
+ function clamp2(x, lo, hi) {
1751
+ return Math.max(lo, Math.min(hi, x));
1752
+ }
1753
+ function g4(d, k) {
1754
+ const v = d[k];
1755
+ return typeof v === "number" && isFinite(v) ? v : 0;
1756
+ }
1757
+ function scoreAccruals(series) {
1758
+ const ratios = [];
1759
+ for (let i = 0; i < series.length; i++) {
1760
+ const d = series[i];
1761
+ const ni = g4(d, "netIncome");
1762
+ const cfo = g4(d, "operatingCashFlow");
1763
+ if (ni === 0 && cfo === 0) continue;
1764
+ const prevAssets = i > 0 ? g4(series[i - 1], "totalAssets") : g4(d, "totalAssets");
1765
+ const avgAssets = (g4(d, "totalAssets") + prevAssets) / 2;
1766
+ if (avgAssets <= 0) continue;
1767
+ ratios.push((ni - cfo) / avgAssets);
1768
+ }
1769
+ if (!ratios.length) return [0.4, ["Accruals ratio: operating cash flow not available (neutral score)"]];
1770
+ const ma = mean4(ratios);
1771
+ const level = clamp2(1 - (ma + 0.05) / 0.15, 0, 1);
1772
+ const stability = Math.max(0, 1 - cv3(ratios));
1773
+ const score = clamp2(0.7 * level + 0.3 * stability, 0, 1);
1774
+ const quality = ma < 0 ? "cash-backed" : ma < 0.05 ? "modest accruals" : "high accruals";
1775
+ return [score, [
1776
+ `Accruals ratio: mean ${(ma * 100).toFixed(1)}% of assets (${quality})`,
1777
+ ma < 0.02 ? "Earnings predominantly backed by cash flows" : "Elevated accruals \u2014 verify cash conversion"
1778
+ ]];
1779
+ }
1780
+ function scoreCashEarnings(series) {
1781
+ const ratios = [];
1782
+ for (const d of series) {
1783
+ const ni = g4(d, "netIncome");
1784
+ const cfo = g4(d, "operatingCashFlow");
1785
+ if (ni > 0) ratios.push(cfo / ni);
1786
+ }
1787
+ if (!ratios.length) return [0.4, ["Cash earnings quality: insufficient CFO/NI data (neutral score)"]];
1788
+ const mr = mean4(ratios);
1789
+ const level = clamp2((mr - 0.5) / 1, 0, 1);
1790
+ const stability = Math.max(0, 1 - cv3(ratios) * 0.8);
1791
+ const score = clamp2(0.65 * level + 0.35 * stability, 0, 1);
1792
+ const above = ratios.filter((r) => r >= 1).length;
1793
+ const quality = mr >= 1.2 ? "excellent" : mr >= 0.9 ? "good" : "weak";
1794
+ return [score, [
1795
+ `CFO/NI conversion: mean ${mr.toFixed(2)}\xD7 (${above}/${ratios.length} years \u2265 1.0)`,
1796
+ `Cash earnings quality: ${quality}`
1797
+ ]];
1798
+ }
1799
+ function scoreRevenueRecognition(series) {
1800
+ const spreads = [];
1801
+ for (let i = 1; i < series.length; i++) {
1802
+ const prev = series[i - 1];
1803
+ const curr = series[i];
1804
+ if (g4(prev, "revenue") <= 0) continue;
1805
+ const revGrowth = (g4(curr, "revenue") - g4(prev, "revenue")) / g4(prev, "revenue");
1806
+ const arPrev = g4(prev, "accountsReceivable");
1807
+ const arCurr = g4(curr, "accountsReceivable");
1808
+ if (arPrev > 0 && arCurr > 0) {
1809
+ spreads.push(revGrowth - (arCurr - arPrev) / arPrev);
1810
+ }
1811
+ }
1812
+ if (!spreads.length) return [0.45, ["Revenue recognition: accounts receivable data unavailable (neutral score)"]];
1813
+ const ms = mean4(spreads);
1814
+ const level = clamp2((ms + 0.15) / 0.25, 0, 1);
1815
+ const consistency = Math.max(0, 1 - cv3(spreads) * 0.5);
1816
+ const score = clamp2(0.65 * level + 0.35 * consistency, 0, 1);
1817
+ const quality = ms > 0.05 ? "conservative" : ms > -0.05 ? "neutral" : "aggressive";
1818
+ const pos = spreads.filter((s) => s >= 0).length;
1819
+ return [score, [
1820
+ `Revenue recognition: revenue-AR spread ${(ms * 100).toFixed(1)}%/yr (${quality})`,
1821
+ `Revenue outpaced receivables in ${pos}/${spreads.length} periods`
1822
+ ]];
1823
+ }
1824
+ function scoreGrossMarginStability(series) {
1825
+ const gms = series.filter((d) => g4(d, "revenue") > 0 && g4(d, "grossProfit") > 0).map((d) => g4(d, "grossProfit") / g4(d, "revenue"));
1826
+ if (gms.length < 2) return [0.45, ["Gross margin stability: insufficient data (neutral score)"]];
1827
+ const mgm = mean4(gms);
1828
+ const cvGm = cv3(gms);
1829
+ const slope = olsSlope3(gms);
1830
+ const stability = clamp2(1 - cvGm * 3, 0, 1);
1831
+ const trendAdj = slope > 5e-3 ? 0.08 : slope < -5e-3 ? -0.08 : 0;
1832
+ const score = clamp2(stability + trendAdj, 0, 1);
1833
+ const trend = slope > 5e-3 ? "improving" : slope < -5e-3 ? "eroding" : "stable";
1834
+ const quality = cvGm < 0.05 ? "high" : cvGm < 0.15 ? "moderate" : "low";
1835
+ return [score, [
1836
+ `Gross margin: mean ${(mgm * 100).toFixed(1)}% stability ${quality} (CV ${cvGm.toFixed(3)})`,
1837
+ `Gross margin trend: ${trend}`
1838
+ ]];
1839
+ }
1840
+ function scoreAssetEfficiency(series) {
1841
+ const tos = series.filter((d) => g4(d, "totalAssets") > 0 && g4(d, "revenue") > 0).map((d) => g4(d, "revenue") / g4(d, "totalAssets"));
1842
+ if (tos.length < 2) return [0.45, ["Asset efficiency: insufficient data (neutral score)"]];
1843
+ const slope = olsSlope3(tos);
1844
+ const mto = mean4(tos);
1845
+ const trendScore = clamp2(0.5 + slope * 5, 0, 1);
1846
+ const level = clamp2((mto - 0.2) / 1.5, 0, 1);
1847
+ const score = clamp2(0.5 * trendScore + 0.5 * level, 0, 1);
1848
+ const dir = slope > 0.02 ? "improving" : slope < -0.02 ? "declining" : "stable";
1849
+ return [score, [`Asset turnover: mean ${mto.toFixed(2)}\xD7 trend ${dir} (slope ${slope.toFixed(3)}/yr)`]];
1850
+ }
1851
+ function earningsQualityScore(annualData, _options = {}) {
1852
+ if (annualData.length < 2) {
1853
+ throw new Error("earningsQualityScore requires at least 2 years of data.");
1854
+ }
1855
+ const [accScore, accEv] = scoreAccruals(annualData);
1856
+ const [cfeScore, cfeEv] = scoreCashEarnings(annualData);
1857
+ const [revScore, revEv] = scoreRevenueRecognition(annualData);
1858
+ const [gmsScore, gmsEv] = scoreGrossMarginStability(annualData);
1859
+ const [effScore, effEv] = scoreAssetEfficiency(annualData);
1860
+ const raw = 0.3 * accScore + 0.25 * cfeScore + 0.2 * revScore + 0.15 * gmsScore + 0.1 * effScore;
1861
+ const score = Math.round(clamp2(raw, 0, 1) * 100);
1862
+ const rating = score >= 75 ? "high" : score >= 50 ? "medium" : score >= 25 ? "low" : "poor";
1863
+ return {
1864
+ score,
1865
+ rating,
1866
+ components: {
1867
+ accrualsRatio: Math.round(accScore * 1e4) / 1e4,
1868
+ cashEarnings: Math.round(cfeScore * 1e4) / 1e4,
1869
+ revenueRecognition: Math.round(revScore * 1e4) / 1e4,
1870
+ grossMarginStability: Math.round(gmsScore * 1e4) / 1e4,
1871
+ assetEfficiency: Math.round(effScore * 1e4) / 1e4
1872
+ },
1873
+ yearsAnalyzed: annualData.length,
1874
+ evidence: [...accEv, ...cfeEv, ...revEv, ...gmsEv, ...effEv]
1875
+ };
1876
+ }
1877
+
1878
+ // src/utils/fair-value.ts
1879
+ function percentile2(xs, p) {
1880
+ if (!xs.length) return 0;
1881
+ const sorted = [...xs].sort((a, b) => a - b);
1882
+ const idx = p / 100 * (sorted.length - 1);
1883
+ const lo = Math.floor(idx);
1884
+ const hi = Math.min(lo + 1, sorted.length - 1);
1885
+ return (sorted[lo] ?? 0) + ((sorted[hi] ?? 0) - (sorted[lo] ?? 0)) * (idx - lo);
1886
+ }
1887
+ function trimmedMean(xs) {
1888
+ if (xs.length <= 3) return xs.reduce((a, b) => a + b, 0) / xs.length;
1889
+ const sorted = [...xs].sort((a, b) => a - b).slice(1, -1);
1890
+ return sorted.reduce((a, b) => a + b, 0) / sorted.length;
1891
+ }
1892
+ function round2(x) {
1893
+ return Math.round(x * 100) / 100;
1894
+ }
1895
+ function dcfValue(fcf, growthRate, terminalGrowth, wacc, shares, years) {
1896
+ if (wacc <= terminalGrowth || fcf <= 0 || shares <= 0) return null;
1897
+ let pv = 0;
1898
+ let cf = fcf;
1899
+ for (let i = 1; i <= years; i++) {
1900
+ cf *= 1 + growthRate;
1901
+ pv += cf / (1 + wacc) ** i;
1902
+ }
1903
+ const terminal = cf * (1 + terminalGrowth) / (wacc - terminalGrowth);
1904
+ pv += terminal / (1 + wacc) ** years;
1905
+ return pv / shares;
1906
+ }
1907
+ function grahamValue(eps, bvps) {
1908
+ return eps > 0 && bvps > 0 ? Math.sqrt(22.5 * eps * bvps) : null;
1909
+ }
1910
+ function fcfYieldValue(fcf, shares, targetYield) {
1911
+ return fcf > 0 && shares > 0 && targetYield > 0 ? fcf / targetYield / shares : null;
1912
+ }
1913
+ function evEbitdaValue(ebitda, totalDebt, cash, shares, multiple) {
1914
+ if (ebitda <= 0 || shares <= 0) return null;
1915
+ const equityValue = ebitda * multiple - totalDebt + cash;
1916
+ return equityValue > 0 ? equityValue / shares : null;
1917
+ }
1918
+ function epvValue(ebit, taxRate, wacc, shares, cash, totalDebt) {
1919
+ if (ebit <= 0 || wacc <= 0 || shares <= 0) return null;
1920
+ const equityValue = ebit * (1 - taxRate) / wacc + cash - totalDebt;
1921
+ return equityValue > 0 ? equityValue / shares : null;
1922
+ }
1923
+ function fairValueRange(options) {
1924
+ const {
1925
+ fcf,
1926
+ shares,
1927
+ growthRate = 0.08,
1928
+ terminalGrowth = 0.03,
1929
+ wacc = 0.09,
1930
+ dcfYears = 10,
1931
+ eps,
1932
+ bvps,
1933
+ targetYield = 0.04,
1934
+ ebitda,
1935
+ totalDebt = 0,
1936
+ cash = 0,
1937
+ evEbitdaMultiple = 12,
1938
+ ebit,
1939
+ taxRate = 0.21,
1940
+ currentPrice
1941
+ } = options;
1942
+ const estimates = {};
1943
+ if (fcf && fcf > 0 && shares && shares > 0) {
1944
+ const v = dcfValue(fcf, growthRate, terminalGrowth, wacc, shares, dcfYears);
1945
+ if (v && v > 0) estimates["DCF (2-stage)"] = v;
1946
+ const fy = fcfYieldValue(fcf, shares, targetYield);
1947
+ if (fy && fy > 0) estimates[`FCF Yield (${(targetYield * 100).toFixed(0)}%)`] = fy;
1948
+ }
1949
+ if (eps && bvps) {
1950
+ const g5 = grahamValue(eps, bvps);
1951
+ if (g5 && g5 > 0) estimates["Graham Number"] = g5;
1952
+ }
1953
+ if (ebitda && ebitda > 0 && shares && shares > 0) {
1954
+ const v = evEbitdaValue(ebitda, totalDebt, cash, shares, evEbitdaMultiple);
1955
+ if (v && v > 0) estimates[`EV/EBITDA (${evEbitdaMultiple.toFixed(0)}\xD7)`] = v;
1956
+ }
1957
+ if (ebit && ebit > 0 && shares && shares > 0) {
1958
+ const v = epvValue(ebit, taxRate, wacc, shares, cash, totalDebt);
1959
+ if (v && v > 0) estimates["Earnings Power Value"] = v;
1960
+ }
1961
+ if (!Object.keys(estimates).length) {
1962
+ throw new Error(
1963
+ "No valuation methods could be computed. Provide at least one of: (fcf + shares), (eps + bvps), (ebitda + shares), (ebit + shares)."
1964
+ );
1965
+ }
1966
+ const vals = Object.values(estimates);
1967
+ const baseValue = round2(trimmedMean(vals));
1968
+ const bearValue = round2(percentile2(vals, 25));
1969
+ const bullValue = round2(percentile2(vals, 75));
1970
+ const result = {
1971
+ estimates: Object.fromEntries(Object.entries(estimates).map(([k, v]) => [k, round2(v)])),
1972
+ baseValue,
1973
+ bearValue,
1974
+ bullValue,
1975
+ methodsUsed: vals.length
1976
+ };
1977
+ if (currentPrice !== void 0 && currentPrice > 0) {
1978
+ result.currentPrice = currentPrice;
1979
+ result.upsidePct = Math.round((baseValue / currentPrice - 1) * 1e3) / 10;
1980
+ result.marginOfSafety = Math.round((bearValue / currentPrice - 1) * 1e3) / 10;
1981
+ }
1982
+ return result;
1983
+ }
1984
+
1985
+ // src/utils/quality-score.ts
1986
+ function qualityScore(annualData, options = {}) {
1987
+ if (annualData.length < 2) {
1988
+ throw new Error("qualityScore requires at least 2 years of data.");
1989
+ }
1990
+ const eq = earningsQualityScore(annualData);
1991
+ const ms = moatScore(annualData, options);
1992
+ const ca = capitalAllocationScore(annualData, options);
1993
+ const eqNorm = eq.score / 100;
1994
+ const msNorm = ms.score / 100;
1995
+ const caNorm = ca.score / 100;
1996
+ const raw = 0.35 * eqNorm + 0.35 * msNorm + 0.3 * caNorm;
1997
+ const score = Math.round(raw * 100);
1998
+ const grade = score >= 80 ? "exceptional" : score >= 60 ? "strong" : score >= 40 ? "moderate" : score >= 20 ? "weak" : "poor";
1999
+ return {
2000
+ score,
2001
+ grade,
2002
+ components: {
2003
+ earningsQuality: Math.round(eqNorm * 1e4) / 1e4,
2004
+ moat: Math.round(msNorm * 1e4) / 1e4,
2005
+ capitalAllocation: Math.round(caNorm * 1e4) / 1e4
2006
+ },
2007
+ subScores: { earningsQuality: eq, moat: ms, capitalAllocation: ca },
2008
+ yearsAnalyzed: annualData.length,
2009
+ evidence: [
2010
+ `Earnings Quality: ${eq.score}/100 [${eq.rating.toUpperCase()}]`,
2011
+ `Economic Moat: ${ms.score}/100 [${ms.width.toUpperCase()}]`,
2012
+ `Capital Allocation: ${ca.score}/100 [${ca.rating.toUpperCase()}]`
2013
+ ]
2014
+ };
2015
+ }
2016
+
2017
+ // src/utils/portfolio.ts
2018
+ function portfolioQuality(holdings, options = {}) {
2019
+ if (!holdings.length) throw new Error("holdings must be a non-empty array.");
2020
+ const totalWeight = holdings.reduce((s, h) => s + h.weight, 0);
2021
+ if (totalWeight <= 0) throw new Error("Sum of holding weights must be positive.");
2022
+ const results = holdings.map((h) => {
2023
+ const normWeight = h.weight / totalWeight;
2024
+ try {
2025
+ const q = qualityScore(h.annualData, options);
2026
+ return { ticker: h.ticker, weight: normWeight, quality: q };
2027
+ } catch (err) {
2028
+ return {
2029
+ ticker: h.ticker,
2030
+ weight: normWeight,
2031
+ quality: null,
2032
+ error: err instanceof Error ? err.message : String(err)
2033
+ };
2034
+ }
2035
+ });
2036
+ const successful = results.filter(
2037
+ (r) => r.quality !== null
2038
+ );
2039
+ if (!successful.length) {
2040
+ throw new Error("All holdings failed to compute. Check data and minimum year requirements.");
2041
+ }
2042
+ const effWeight = successful.reduce((s, h) => s + h.weight, 0);
2043
+ const renorm = effWeight > 0 ? 1 / effWeight : 1;
2044
+ const wavg = (fn) => Math.round(successful.reduce((s, h) => s + h.weight * fn(h.quality) * renorm, 0) * 10) / 10;
2045
+ const wq = wavg((q) => q.score);
2046
+ const wm = wavg((q) => q.subScores.moat.score);
2047
+ const weq = wavg((q) => q.subScores.earningsQuality.score);
2048
+ const wca = wavg((q) => q.subScores.capitalAllocation.score);
2049
+ const grade = wq >= 80 ? "exceptional" : wq >= 60 ? "strong" : wq >= 40 ? "moderate" : wq >= 20 ? "weak" : "poor";
2050
+ return {
2051
+ holdings: results,
2052
+ weightedQualityScore: wq,
2053
+ weightedMoatScore: wm,
2054
+ weightedEarningsQuality: weq,
2055
+ weightedCapitalAllocation: wca,
2056
+ effectiveWeight: Math.round(effWeight * 1e4) / 1e4,
2057
+ grade,
2058
+ errors: results.filter((r) => r.error).map((r) => `${r.ticker}: ${r.error}`)
2059
+ };
2060
+ }
2061
+
1728
2062
  exports.affo = affo;
1729
2063
  exports.altmanZScore = altmanZScore;
1730
2064
  exports.annualizeReturn = annualizeReturn;
@@ -1768,6 +2102,7 @@ exports.dpo = dpo;
1768
2102
  exports.dso = dso;
1769
2103
  exports.duPont3 = duPont3;
1770
2104
  exports.earningsPowerValue = earningsPowerValue;
2105
+ exports.earningsQualityScore = earningsQualityScore;
1771
2106
  exports.ebitdaCoverageRatio = ebitdaCoverageRatio;
1772
2107
  exports.ebitdaGrowth = ebitdaGrowth;
1773
2108
  exports.ebitdaMargin = ebitdaMargin;
@@ -1781,6 +2116,7 @@ exports.evFcf = evFcf;
1781
2116
  exports.evInvestedCapital = evInvestedCapital;
1782
2117
  exports.evRevenue = evRevenue;
1783
2118
  exports.expenseRatio = expenseRatio;
2119
+ exports.fairValueRange = fairValueRange;
1784
2120
  exports.fcfConversion = fcfConversion;
1785
2121
  exports.fcfGrowth = fcfGrowth;
1786
2122
  exports.fcfMargin = fcfMargin;
@@ -1840,11 +2176,13 @@ exports.pe = pe;
1840
2176
  exports.peg = peg;
1841
2177
  exports.percentile = percentile;
1842
2178
  exports.piotroskiFScore = piotroskiFScore;
2179
+ exports.portfolioQuality = portfolioQuality;
1843
2180
  exports.premiumsToSurplus = premiumsToSurplus;
1844
2181
  exports.pricesToReturns = pricesToReturns;
1845
2182
  exports.profitPerEmployee = profitPerEmployee;
1846
2183
  exports.provisionCoverageRatio = provisionCoverageRatio;
1847
2184
  exports.ps = ps;
2185
+ exports.qualityScore = qualityScore;
1848
2186
  exports.quickRatio = quickRatio;
1849
2187
  exports.receivablesTurnover = receivablesTurnover;
1850
2188
  exports.revenueCAGR = revenueCAGR;