fin-ratios 0.1.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.
- package/dist/fetchers/edgar/index.cjs +100 -0
- package/dist/fetchers/edgar/index.cjs.map +1 -0
- package/dist/fetchers/edgar/index.d.cts +33 -0
- package/dist/fetchers/edgar/index.d.ts +33 -0
- package/dist/fetchers/edgar/index.js +98 -0
- package/dist/fetchers/edgar/index.js.map +1 -0
- package/dist/fetchers/fmp/index.cjs +110 -0
- package/dist/fetchers/fmp/index.cjs.map +1 -0
- package/dist/fetchers/fmp/index.d.cts +36 -0
- package/dist/fetchers/fmp/index.d.ts +36 -0
- package/dist/fetchers/fmp/index.js +107 -0
- package/dist/fetchers/fmp/index.js.map +1 -0
- package/dist/fetchers/yahoo/index.cjs +134 -0
- package/dist/fetchers/yahoo/index.cjs.map +1 -0
- package/dist/fetchers/yahoo/index.d.cts +50 -0
- package/dist/fetchers/yahoo/index.d.ts +50 -0
- package/dist/fetchers/yahoo/index.js +131 -0
- package/dist/fetchers/yahoo/index.js.map +1 -0
- package/dist/financials-DUHxWDPS.d.cts +113 -0
- package/dist/financials-DUHxWDPS.d.ts +113 -0
- package/dist/index.cjs +1180 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +1314 -0
- package/dist/index.d.ts +1314 -0
- package/dist/index.js +1040 -0
- package/dist/index.js.map +1 -0
- package/dist/market-DlMw3vK1.d.cts +43 -0
- package/dist/market-DlMw3vK1.d.ts +43 -0
- package/package.json +87 -0
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// src/fetchers/edgar/index.ts
|
|
4
|
+
var COMPANY_TICKERS_URL = "https://data.sec.gov/files/company_tickers.json";
|
|
5
|
+
var EDGAR_BASE = "https://data.sec.gov/api/xbrl/companyfacts";
|
|
6
|
+
var HEADERS = {
|
|
7
|
+
"User-Agent": "fin-ratios/0.1.0 contact@fin-ratios.dev",
|
|
8
|
+
"Accept-Encoding": "gzip, deflate"
|
|
9
|
+
};
|
|
10
|
+
async function fetchEdgar(ticker, options = {}) {
|
|
11
|
+
const { numYears = 3 } = options;
|
|
12
|
+
const tickerMap = await _get(COMPANY_TICKERS_URL);
|
|
13
|
+
const entry = Object.values(tickerMap).find((v) => v.ticker.toUpperCase() === ticker.toUpperCase());
|
|
14
|
+
if (!entry) throw new Error(`EDGAR: ticker ${ticker} not found`);
|
|
15
|
+
const cik = String(entry.cik_str).padStart(10, "0");
|
|
16
|
+
const facts = await _get(`${EDGAR_BASE}/CIK${cik}.json`);
|
|
17
|
+
const gaap = facts.facts["us-gaap"] ?? {};
|
|
18
|
+
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
|
+
}
|
|
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;
|
|
26
|
+
}
|
|
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({
|
|
50
|
+
fiscalYear: year,
|
|
51
|
+
income: {
|
|
52
|
+
revenue: rev,
|
|
53
|
+
grossProfit: gp,
|
|
54
|
+
cogs: rev - gp,
|
|
55
|
+
ebit,
|
|
56
|
+
ebitda: 0,
|
|
57
|
+
netIncome: ni,
|
|
58
|
+
interestExpense: Math.abs(int),
|
|
59
|
+
incomeTaxExpense: tax,
|
|
60
|
+
ebt: ni + tax,
|
|
61
|
+
depreciationAndAmortization: 0,
|
|
62
|
+
sharesOutstanding: 0,
|
|
63
|
+
eps: 0
|
|
64
|
+
},
|
|
65
|
+
balance: {
|
|
66
|
+
totalAssets: ta,
|
|
67
|
+
currentAssets: ca,
|
|
68
|
+
cash,
|
|
69
|
+
accountsReceivable: ar,
|
|
70
|
+
inventory: inv,
|
|
71
|
+
netPPE: 0,
|
|
72
|
+
goodwill: 0,
|
|
73
|
+
retainedEarnings: re,
|
|
74
|
+
totalLiabilities: tl,
|
|
75
|
+
currentLiabilities: cl,
|
|
76
|
+
accountsPayable: 0,
|
|
77
|
+
longTermDebt: ltd,
|
|
78
|
+
totalDebt: ltd,
|
|
79
|
+
totalEquity: te,
|
|
80
|
+
sharesOutstanding: 0
|
|
81
|
+
},
|
|
82
|
+
cashFlow: {
|
|
83
|
+
operatingCashFlow: ocf,
|
|
84
|
+
capex: Math.abs(capex),
|
|
85
|
+
investingCashFlow: 0,
|
|
86
|
+
financingCashFlow: 0
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
return results;
|
|
91
|
+
}
|
|
92
|
+
async function _get(url) {
|
|
93
|
+
const resp = await fetch(url, { headers: HEADERS });
|
|
94
|
+
if (!resp.ok) throw new Error(`EDGAR request failed: ${resp.status} ${url}`);
|
|
95
|
+
return resp.json();
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
exports.fetchEdgar = fetchEdgar;
|
|
99
|
+
//# sourceMappingURL=index.cjs.map
|
|
100
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +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"]}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { I as IncomeStatement, B as BalanceSheet, C as CashFlowStatement } from '../../financials-DUHxWDPS.cjs';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* SEC EDGAR fetcher for fin-ratios (TypeScript).
|
|
5
|
+
*
|
|
6
|
+
* Fetches financial data from the free SEC EDGAR XBRL API.
|
|
7
|
+
* No API key required. US companies only.
|
|
8
|
+
*
|
|
9
|
+
* Rate limit: ~10 requests/second (SEC guidelines). Add delays for bulk use.
|
|
10
|
+
*
|
|
11
|
+
* @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
|
+
* }
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
interface EdgarOptions {
|
|
20
|
+
numYears?: number;
|
|
21
|
+
}
|
|
22
|
+
interface EdgarFilingData {
|
|
23
|
+
fiscalYear: string;
|
|
24
|
+
income: IncomeStatement;
|
|
25
|
+
balance: BalanceSheet;
|
|
26
|
+
cashFlow: CashFlowStatement;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Fetch multi-year annual filings from SEC EDGAR for a US stock ticker.
|
|
30
|
+
*/
|
|
31
|
+
declare function fetchEdgar(ticker: string, options?: EdgarOptions): Promise<EdgarFilingData[]>;
|
|
32
|
+
|
|
33
|
+
export { type EdgarFilingData, type EdgarOptions, fetchEdgar };
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { I as IncomeStatement, B as BalanceSheet, C as CashFlowStatement } from '../../financials-DUHxWDPS.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* SEC EDGAR fetcher for fin-ratios (TypeScript).
|
|
5
|
+
*
|
|
6
|
+
* Fetches financial data from the free SEC EDGAR XBRL API.
|
|
7
|
+
* No API key required. US companies only.
|
|
8
|
+
*
|
|
9
|
+
* Rate limit: ~10 requests/second (SEC guidelines). Add delays for bulk use.
|
|
10
|
+
*
|
|
11
|
+
* @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
|
+
* }
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
interface EdgarOptions {
|
|
20
|
+
numYears?: number;
|
|
21
|
+
}
|
|
22
|
+
interface EdgarFilingData {
|
|
23
|
+
fiscalYear: string;
|
|
24
|
+
income: IncomeStatement;
|
|
25
|
+
balance: BalanceSheet;
|
|
26
|
+
cashFlow: CashFlowStatement;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Fetch multi-year annual filings from SEC EDGAR for a US stock ticker.
|
|
30
|
+
*/
|
|
31
|
+
declare function fetchEdgar(ticker: string, options?: EdgarOptions): Promise<EdgarFilingData[]>;
|
|
32
|
+
|
|
33
|
+
export { type EdgarFilingData, type EdgarOptions, fetchEdgar };
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
// src/fetchers/edgar/index.ts
|
|
2
|
+
var COMPANY_TICKERS_URL = "https://data.sec.gov/files/company_tickers.json";
|
|
3
|
+
var EDGAR_BASE = "https://data.sec.gov/api/xbrl/companyfacts";
|
|
4
|
+
var HEADERS = {
|
|
5
|
+
"User-Agent": "fin-ratios/0.1.0 contact@fin-ratios.dev",
|
|
6
|
+
"Accept-Encoding": "gzip, deflate"
|
|
7
|
+
};
|
|
8
|
+
async function fetchEdgar(ticker, options = {}) {
|
|
9
|
+
const { numYears = 3 } = options;
|
|
10
|
+
const tickerMap = await _get(COMPANY_TICKERS_URL);
|
|
11
|
+
const entry = Object.values(tickerMap).find((v) => v.ticker.toUpperCase() === ticker.toUpperCase());
|
|
12
|
+
if (!entry) throw new Error(`EDGAR: ticker ${ticker} not found`);
|
|
13
|
+
const cik = String(entry.cik_str).padStart(10, "0");
|
|
14
|
+
const facts = await _get(`${EDGAR_BASE}/CIK${cik}.json`);
|
|
15
|
+
const gaap = facts.facts["us-gaap"] ?? {};
|
|
16
|
+
function _annual(concept) {
|
|
17
|
+
const entries = gaap[concept]?.units?.USD ?? [];
|
|
18
|
+
return entries.filter((e) => e.form === "10-K").sort((a, b) => b.end.localeCompare(a.end));
|
|
19
|
+
}
|
|
20
|
+
function _latestForYear(concept, year) {
|
|
21
|
+
const annual = _annual(concept);
|
|
22
|
+
const match = annual.find((e) => e.end.startsWith(year));
|
|
23
|
+
return match?.val ?? 0;
|
|
24
|
+
}
|
|
25
|
+
const revenueEntries = _annual("Revenues").concat(_annual("RevenueFromContractWithCustomerExcludingAssessedTax"));
|
|
26
|
+
const years = [...new Set(revenueEntries.map((e) => e.end.slice(0, 4)))].slice(0, numYears);
|
|
27
|
+
const results = [];
|
|
28
|
+
for (const year of years) {
|
|
29
|
+
const rev = _latestForYear("Revenues", year) || _latestForYear("RevenueFromContractWithCustomerExcludingAssessedTax", year);
|
|
30
|
+
const ni = _latestForYear("NetIncomeLoss", year);
|
|
31
|
+
const ebit = _latestForYear("OperatingIncomeLoss", year);
|
|
32
|
+
const ta = _latestForYear("Assets", year);
|
|
33
|
+
const ca = _latestForYear("AssetsCurrent", year);
|
|
34
|
+
const cash = _latestForYear("CashAndCashEquivalentsAtCarryingValue", year);
|
|
35
|
+
const ar = _latestForYear("AccountsReceivableNetCurrent", year);
|
|
36
|
+
const inv = _latestForYear("InventoryNet", year);
|
|
37
|
+
const cl = _latestForYear("LiabilitiesCurrent", year);
|
|
38
|
+
const tl = _latestForYear("Liabilities", year);
|
|
39
|
+
const ltd = _latestForYear("LongTermDebt", year);
|
|
40
|
+
const re = _latestForYear("RetainedEarningsAccumulatedDeficit", year);
|
|
41
|
+
const te = _latestForYear("StockholdersEquity", year);
|
|
42
|
+
const ocf = _latestForYear("NetCashProvidedByUsedInOperatingActivities", year);
|
|
43
|
+
const capex = _latestForYear("PaymentsToAcquirePropertyPlantAndEquipment", year);
|
|
44
|
+
const gp = _latestForYear("GrossProfit", year);
|
|
45
|
+
const int = _latestForYear("InterestExpense", year);
|
|
46
|
+
const tax = _latestForYear("IncomeTaxExpense", year) || _latestForYear("IncomeTaxesPaidNet", year);
|
|
47
|
+
results.push({
|
|
48
|
+
fiscalYear: year,
|
|
49
|
+
income: {
|
|
50
|
+
revenue: rev,
|
|
51
|
+
grossProfit: gp,
|
|
52
|
+
cogs: rev - gp,
|
|
53
|
+
ebit,
|
|
54
|
+
ebitda: 0,
|
|
55
|
+
netIncome: ni,
|
|
56
|
+
interestExpense: Math.abs(int),
|
|
57
|
+
incomeTaxExpense: tax,
|
|
58
|
+
ebt: ni + tax,
|
|
59
|
+
depreciationAndAmortization: 0,
|
|
60
|
+
sharesOutstanding: 0,
|
|
61
|
+
eps: 0
|
|
62
|
+
},
|
|
63
|
+
balance: {
|
|
64
|
+
totalAssets: ta,
|
|
65
|
+
currentAssets: ca,
|
|
66
|
+
cash,
|
|
67
|
+
accountsReceivable: ar,
|
|
68
|
+
inventory: inv,
|
|
69
|
+
netPPE: 0,
|
|
70
|
+
goodwill: 0,
|
|
71
|
+
retainedEarnings: re,
|
|
72
|
+
totalLiabilities: tl,
|
|
73
|
+
currentLiabilities: cl,
|
|
74
|
+
accountsPayable: 0,
|
|
75
|
+
longTermDebt: ltd,
|
|
76
|
+
totalDebt: ltd,
|
|
77
|
+
totalEquity: te,
|
|
78
|
+
sharesOutstanding: 0
|
|
79
|
+
},
|
|
80
|
+
cashFlow: {
|
|
81
|
+
operatingCashFlow: ocf,
|
|
82
|
+
capex: Math.abs(capex),
|
|
83
|
+
investingCashFlow: 0,
|
|
84
|
+
financingCashFlow: 0
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
return results;
|
|
89
|
+
}
|
|
90
|
+
async function _get(url) {
|
|
91
|
+
const resp = await fetch(url, { headers: HEADERS });
|
|
92
|
+
if (!resp.ok) throw new Error(`EDGAR request failed: ${resp.status} ${url}`);
|
|
93
|
+
return resp.json();
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export { fetchEdgar };
|
|
97
|
+
//# sourceMappingURL=index.js.map
|
|
98
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +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"]}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// src/fetchers/fmp/index.ts
|
|
4
|
+
var FMP_BASE = "https://financialmodelingprep.com/api/v3";
|
|
5
|
+
async function fetchFmp(ticker, options) {
|
|
6
|
+
const { apiKey, periods = 4, quarterly = false } = options;
|
|
7
|
+
const period = quarterly ? "quarter" : "annual";
|
|
8
|
+
const limit = periods;
|
|
9
|
+
const [incData, balData, cfData, ratioData, profileData] = await Promise.all([
|
|
10
|
+
_get(`${FMP_BASE}/income-statement/${ticker}`, apiKey, { period, limit }),
|
|
11
|
+
_get(`${FMP_BASE}/balance-sheet-statement/${ticker}`, apiKey, { period, limit }),
|
|
12
|
+
_get(`${FMP_BASE}/cash-flow-statement/${ticker}`, apiKey, { period, limit }),
|
|
13
|
+
_get(`${FMP_BASE}/ratios/${ticker}`, apiKey, { period, limit }),
|
|
14
|
+
_get(`${FMP_BASE}/profile/${ticker}`, apiKey, {})
|
|
15
|
+
]);
|
|
16
|
+
const profile = profileData[0] ?? {};
|
|
17
|
+
const results = [];
|
|
18
|
+
for (let i = 0; i < Math.min(limit, incData.length); i++) {
|
|
19
|
+
const inc = incData[i] ?? {};
|
|
20
|
+
const bal = balData[i] ?? {};
|
|
21
|
+
const cf = cfData[i] ?? {};
|
|
22
|
+
const rat = ratioData[i] ?? {};
|
|
23
|
+
results.push({
|
|
24
|
+
period: inc.date ?? "",
|
|
25
|
+
income: {
|
|
26
|
+
revenue: _n(inc.revenue),
|
|
27
|
+
grossProfit: _n(inc.grossProfit),
|
|
28
|
+
cogs: _n(inc.costOfRevenue),
|
|
29
|
+
ebit: _n(inc.operatingIncome),
|
|
30
|
+
ebitda: _n(inc.ebitda),
|
|
31
|
+
netIncome: _n(inc.netIncome),
|
|
32
|
+
interestExpense: Math.abs(_n(inc.interestExpense)),
|
|
33
|
+
incomeTaxExpense: _n(inc.incomeTaxExpense),
|
|
34
|
+
ebt: _n(inc.incomeBeforeTax),
|
|
35
|
+
depreciationAndAmortization: _n(inc.depreciationAndAmortization),
|
|
36
|
+
sharesOutstanding: _n(inc.weightedAverageShsOut),
|
|
37
|
+
eps: _n(inc.eps),
|
|
38
|
+
epsDiluted: _n(inc.epsdiluted)
|
|
39
|
+
},
|
|
40
|
+
balance: {
|
|
41
|
+
totalAssets: _n(bal.totalAssets),
|
|
42
|
+
currentAssets: _n(bal.totalCurrentAssets),
|
|
43
|
+
cash: _n(bal.cashAndCashEquivalents),
|
|
44
|
+
accountsReceivable: _n(bal.netReceivables),
|
|
45
|
+
inventory: _n(bal.inventory),
|
|
46
|
+
netPPE: _n(bal.propertyPlantEquipmentNet),
|
|
47
|
+
goodwill: _n(bal.goodwill),
|
|
48
|
+
intangibleAssets: _n(bal.intangibleAssets),
|
|
49
|
+
retainedEarnings: _n(bal.retainedEarnings),
|
|
50
|
+
totalLiabilities: _n(bal.totalLiabilities),
|
|
51
|
+
currentLiabilities: _n(bal.totalCurrentLiabilities),
|
|
52
|
+
accountsPayable: _n(bal.accountPayables),
|
|
53
|
+
longTermDebt: _n(bal.longTermDebt),
|
|
54
|
+
totalDebt: _n(bal.totalDebt),
|
|
55
|
+
totalEquity: _n(bal.totalStockholdersEquity),
|
|
56
|
+
sharesOutstanding: _n(inc.weightedAverageShsOut)
|
|
57
|
+
},
|
|
58
|
+
cashFlow: {
|
|
59
|
+
operatingCashFlow: _n(cf.operatingCashFlow),
|
|
60
|
+
capex: Math.abs(_n(cf.capitalExpenditure)),
|
|
61
|
+
investingCashFlow: _n(cf.investingActivitiesCashFlow),
|
|
62
|
+
financingCashFlow: _n(cf.financingActivitiesCashFlow),
|
|
63
|
+
dividendsPaid: _n(cf.commonDividendsPaid),
|
|
64
|
+
stockBasedCompensation: _n(cf.stockBasedCompensation),
|
|
65
|
+
debtRepayments: _n(cf.debtRepayment)
|
|
66
|
+
},
|
|
67
|
+
marketData: {
|
|
68
|
+
price: _n(profile.price),
|
|
69
|
+
marketCap: _n(profile.mktCap),
|
|
70
|
+
sharesOutstanding: _n(inc.weightedAverageShsOut),
|
|
71
|
+
ticker
|
|
72
|
+
},
|
|
73
|
+
ratios: {
|
|
74
|
+
peRatio: _n(rat.priceEarningsRatio),
|
|
75
|
+
pbRatio: _n(rat.priceToBookRatio),
|
|
76
|
+
psRatio: _n(rat.priceToSalesRatio),
|
|
77
|
+
evToEbitda: _n(rat.enterpriseValueMultiple),
|
|
78
|
+
roic: _n(rat.returnOnCapitalEmployed),
|
|
79
|
+
roe: _n(rat.returnOnEquity),
|
|
80
|
+
roa: _n(rat.returnOnAssets),
|
|
81
|
+
currentRatio: _n(rat.currentRatio),
|
|
82
|
+
debtToEquity: _n(rat.debtEquityRatio),
|
|
83
|
+
grossMargin: _n(rat.grossProfitMargin),
|
|
84
|
+
operatingMargin: _n(rat.operatingProfitMargin),
|
|
85
|
+
netMargin: _n(rat.netProfitMargin)
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
return results;
|
|
90
|
+
}
|
|
91
|
+
async function fetchFmpSp500(apiKey) {
|
|
92
|
+
const data = await _get(`${FMP_BASE}/sp500_constituent`, apiKey, {});
|
|
93
|
+
return data.map((d) => d.symbol).filter(Boolean);
|
|
94
|
+
}
|
|
95
|
+
async function _get(url, apiKey, params) {
|
|
96
|
+
const query = new URLSearchParams({ ...Object.fromEntries(Object.entries(params).map(([k, v]) => [k, String(v)])), apikey: apiKey });
|
|
97
|
+
const resp = await fetch(`${url}?${query}`);
|
|
98
|
+
if (!resp.ok) throw new Error(`FMP request failed: ${resp.status} ${url}`);
|
|
99
|
+
return resp.json();
|
|
100
|
+
}
|
|
101
|
+
function _n(v) {
|
|
102
|
+
if (v == null) return 0;
|
|
103
|
+
const n = Number(v);
|
|
104
|
+
return isNaN(n) ? 0 : n;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
exports.fetchFmp = fetchFmp;
|
|
108
|
+
exports.fetchFmpSp500 = fetchFmpSp500;
|
|
109
|
+
//# sourceMappingURL=index.cjs.map
|
|
110
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/fetchers/fmp/index.ts"],"names":[],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Financial Modeling Prep (FMP) fetcher.\n * Free tier: 250 requests/day.\n * Get API key: https://financialmodelingprep.com/developer/docs\n */\n\nimport type {\n IncomeStatement,\n BalanceSheet,\n CashFlowStatement,\n MarketData,\n} from '../../types/index.js'\n\nconst FMP_BASE = 'https://financialmodelingprep.com/api/v3'\n\nexport interface FmpOptions {\n apiKey: string\n periods?: number\n quarterly?: boolean\n}\n\nexport interface FmpPeriodData {\n period: string\n income: IncomeStatement\n balance: BalanceSheet\n cashFlow: CashFlowStatement\n marketData: MarketData\n ratios?: Record<string, number | null>\n}\n\n/**\n * Fetch financial statements from Financial Modeling Prep.\n *\n * @example\n * const data = await fetchFmp('AAPL', { apiKey: 'your_key', periods: 4 })\n * for (const period of data) {\n * const roicVal = roic({ nopat: ..., investedCapital: ... })\n * }\n */\nexport async function fetchFmp(\n ticker: string,\n options: FmpOptions\n): Promise<FmpPeriodData[]> {\n const { apiKey, periods = 4, quarterly = false } = options\n const period = quarterly ? 'quarter' : 'annual'\n const limit = periods\n\n const [incData, balData, cfData, ratioData, profileData] = await Promise.all([\n _get(`${FMP_BASE}/income-statement/${ticker}`, apiKey, { period, limit }),\n _get(`${FMP_BASE}/balance-sheet-statement/${ticker}`, apiKey, { period, limit }),\n _get(`${FMP_BASE}/cash-flow-statement/${ticker}`, apiKey, { period, limit }),\n _get(`${FMP_BASE}/ratios/${ticker}`, apiKey, { period, limit }),\n _get(`${FMP_BASE}/profile/${ticker}`, apiKey, {}),\n ])\n\n const profile = (profileData as any[])[0] ?? {}\n const results: FmpPeriodData[] = []\n\n for (let i = 0; i < Math.min(limit, (incData as any[]).length); i++) {\n const inc = (incData as any[])[i] ?? {}\n const bal = (balData as any[])[i] ?? {}\n const cf = (cfData as any[])[i] ?? {}\n const rat = (ratioData as any[])[i] ?? {}\n\n results.push({\n period: inc.date ?? '',\n income: {\n revenue: _n(inc.revenue),\n grossProfit: _n(inc.grossProfit),\n cogs: _n(inc.costOfRevenue),\n ebit: _n(inc.operatingIncome),\n ebitda: _n(inc.ebitda),\n netIncome: _n(inc.netIncome),\n interestExpense: Math.abs(_n(inc.interestExpense)),\n incomeTaxExpense: _n(inc.incomeTaxExpense),\n ebt: _n(inc.incomeBeforeTax),\n depreciationAndAmortization: _n(inc.depreciationAndAmortization),\n sharesOutstanding: _n(inc.weightedAverageShsOut),\n eps: _n(inc.eps),\n epsDiluted: _n(inc.epsdiluted),\n },\n balance: {\n totalAssets: _n(bal.totalAssets),\n currentAssets: _n(bal.totalCurrentAssets),\n cash: _n(bal.cashAndCashEquivalents),\n accountsReceivable: _n(bal.netReceivables),\n inventory: _n(bal.inventory),\n netPPE: _n(bal.propertyPlantEquipmentNet),\n goodwill: _n(bal.goodwill),\n intangibleAssets: _n(bal.intangibleAssets),\n retainedEarnings: _n(bal.retainedEarnings),\n totalLiabilities: _n(bal.totalLiabilities),\n currentLiabilities: _n(bal.totalCurrentLiabilities),\n accountsPayable: _n(bal.accountPayables),\n longTermDebt: _n(bal.longTermDebt),\n totalDebt: _n(bal.totalDebt),\n totalEquity: _n(bal.totalStockholdersEquity),\n sharesOutstanding: _n(inc.weightedAverageShsOut),\n },\n cashFlow: {\n operatingCashFlow: _n(cf.operatingCashFlow),\n capex: Math.abs(_n(cf.capitalExpenditure)),\n investingCashFlow: _n(cf.investingActivitiesCashFlow),\n financingCashFlow: _n(cf.financingActivitiesCashFlow),\n dividendsPaid: _n(cf.commonDividendsPaid),\n stockBasedCompensation: _n(cf.stockBasedCompensation),\n debtRepayments: _n(cf.debtRepayment),\n },\n marketData: {\n price: _n(profile.price),\n marketCap: _n(profile.mktCap),\n sharesOutstanding: _n(inc.weightedAverageShsOut),\n ticker,\n },\n ratios: {\n peRatio: _n(rat.priceEarningsRatio),\n pbRatio: _n(rat.priceToBookRatio),\n psRatio: _n(rat.priceToSalesRatio),\n evToEbitda: _n(rat.enterpriseValueMultiple),\n roic: _n(rat.returnOnCapitalEmployed),\n roe: _n(rat.returnOnEquity),\n roa: _n(rat.returnOnAssets),\n currentRatio: _n(rat.currentRatio),\n debtToEquity: _n(rat.debtEquityRatio),\n grossMargin: _n(rat.grossProfitMargin),\n operatingMargin: _n(rat.operatingProfitMargin),\n netMargin: _n(rat.netProfitMargin),\n },\n })\n }\n\n return results\n}\n\n/** Fetch the current S&P 500 constituent list. */\nexport async function fetchFmpSp500(apiKey: string): Promise<string[]> {\n const data = (await _get(`${FMP_BASE}/sp500_constituent`, apiKey, {})) as any[]\n return data.map(d => d.symbol).filter(Boolean)\n}\n\nasync function _get(url: string, apiKey: string, params: Record<string, unknown>): Promise<unknown> {\n const query = new URLSearchParams({ ...Object.fromEntries(Object.entries(params).map(([k, v]) => [k, String(v)])), apikey: apiKey })\n const resp = await fetch(`${url}?${query}`)\n if (!resp.ok) throw new Error(`FMP request failed: ${resp.status} ${url}`)\n return resp.json()\n}\n\nfunction _n(v: unknown): number {\n if (v == null) return 0\n const n = Number(v)\n return isNaN(n) ? 0 : n\n}\n"]}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { I as IncomeStatement, B as BalanceSheet, C as CashFlowStatement } from '../../financials-DUHxWDPS.cjs';
|
|
2
|
+
import { M as MarketData } from '../../market-DlMw3vK1.cjs';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Financial Modeling Prep (FMP) fetcher.
|
|
6
|
+
* Free tier: 250 requests/day.
|
|
7
|
+
* Get API key: https://financialmodelingprep.com/developer/docs
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
interface FmpOptions {
|
|
11
|
+
apiKey: string;
|
|
12
|
+
periods?: number;
|
|
13
|
+
quarterly?: boolean;
|
|
14
|
+
}
|
|
15
|
+
interface FmpPeriodData {
|
|
16
|
+
period: string;
|
|
17
|
+
income: IncomeStatement;
|
|
18
|
+
balance: BalanceSheet;
|
|
19
|
+
cashFlow: CashFlowStatement;
|
|
20
|
+
marketData: MarketData;
|
|
21
|
+
ratios?: Record<string, number | null>;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Fetch financial statements from Financial Modeling Prep.
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* const data = await fetchFmp('AAPL', { apiKey: 'your_key', periods: 4 })
|
|
28
|
+
* for (const period of data) {
|
|
29
|
+
* const roicVal = roic({ nopat: ..., investedCapital: ... })
|
|
30
|
+
* }
|
|
31
|
+
*/
|
|
32
|
+
declare function fetchFmp(ticker: string, options: FmpOptions): Promise<FmpPeriodData[]>;
|
|
33
|
+
/** Fetch the current S&P 500 constituent list. */
|
|
34
|
+
declare function fetchFmpSp500(apiKey: string): Promise<string[]>;
|
|
35
|
+
|
|
36
|
+
export { type FmpOptions, type FmpPeriodData, fetchFmp, fetchFmpSp500 };
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { I as IncomeStatement, B as BalanceSheet, C as CashFlowStatement } from '../../financials-DUHxWDPS.js';
|
|
2
|
+
import { M as MarketData } from '../../market-DlMw3vK1.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Financial Modeling Prep (FMP) fetcher.
|
|
6
|
+
* Free tier: 250 requests/day.
|
|
7
|
+
* Get API key: https://financialmodelingprep.com/developer/docs
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
interface FmpOptions {
|
|
11
|
+
apiKey: string;
|
|
12
|
+
periods?: number;
|
|
13
|
+
quarterly?: boolean;
|
|
14
|
+
}
|
|
15
|
+
interface FmpPeriodData {
|
|
16
|
+
period: string;
|
|
17
|
+
income: IncomeStatement;
|
|
18
|
+
balance: BalanceSheet;
|
|
19
|
+
cashFlow: CashFlowStatement;
|
|
20
|
+
marketData: MarketData;
|
|
21
|
+
ratios?: Record<string, number | null>;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Fetch financial statements from Financial Modeling Prep.
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* const data = await fetchFmp('AAPL', { apiKey: 'your_key', periods: 4 })
|
|
28
|
+
* for (const period of data) {
|
|
29
|
+
* const roicVal = roic({ nopat: ..., investedCapital: ... })
|
|
30
|
+
* }
|
|
31
|
+
*/
|
|
32
|
+
declare function fetchFmp(ticker: string, options: FmpOptions): Promise<FmpPeriodData[]>;
|
|
33
|
+
/** Fetch the current S&P 500 constituent list. */
|
|
34
|
+
declare function fetchFmpSp500(apiKey: string): Promise<string[]>;
|
|
35
|
+
|
|
36
|
+
export { type FmpOptions, type FmpPeriodData, fetchFmp, fetchFmpSp500 };
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
// src/fetchers/fmp/index.ts
|
|
2
|
+
var FMP_BASE = "https://financialmodelingprep.com/api/v3";
|
|
3
|
+
async function fetchFmp(ticker, options) {
|
|
4
|
+
const { apiKey, periods = 4, quarterly = false } = options;
|
|
5
|
+
const period = quarterly ? "quarter" : "annual";
|
|
6
|
+
const limit = periods;
|
|
7
|
+
const [incData, balData, cfData, ratioData, profileData] = await Promise.all([
|
|
8
|
+
_get(`${FMP_BASE}/income-statement/${ticker}`, apiKey, { period, limit }),
|
|
9
|
+
_get(`${FMP_BASE}/balance-sheet-statement/${ticker}`, apiKey, { period, limit }),
|
|
10
|
+
_get(`${FMP_BASE}/cash-flow-statement/${ticker}`, apiKey, { period, limit }),
|
|
11
|
+
_get(`${FMP_BASE}/ratios/${ticker}`, apiKey, { period, limit }),
|
|
12
|
+
_get(`${FMP_BASE}/profile/${ticker}`, apiKey, {})
|
|
13
|
+
]);
|
|
14
|
+
const profile = profileData[0] ?? {};
|
|
15
|
+
const results = [];
|
|
16
|
+
for (let i = 0; i < Math.min(limit, incData.length); i++) {
|
|
17
|
+
const inc = incData[i] ?? {};
|
|
18
|
+
const bal = balData[i] ?? {};
|
|
19
|
+
const cf = cfData[i] ?? {};
|
|
20
|
+
const rat = ratioData[i] ?? {};
|
|
21
|
+
results.push({
|
|
22
|
+
period: inc.date ?? "",
|
|
23
|
+
income: {
|
|
24
|
+
revenue: _n(inc.revenue),
|
|
25
|
+
grossProfit: _n(inc.grossProfit),
|
|
26
|
+
cogs: _n(inc.costOfRevenue),
|
|
27
|
+
ebit: _n(inc.operatingIncome),
|
|
28
|
+
ebitda: _n(inc.ebitda),
|
|
29
|
+
netIncome: _n(inc.netIncome),
|
|
30
|
+
interestExpense: Math.abs(_n(inc.interestExpense)),
|
|
31
|
+
incomeTaxExpense: _n(inc.incomeTaxExpense),
|
|
32
|
+
ebt: _n(inc.incomeBeforeTax),
|
|
33
|
+
depreciationAndAmortization: _n(inc.depreciationAndAmortization),
|
|
34
|
+
sharesOutstanding: _n(inc.weightedAverageShsOut),
|
|
35
|
+
eps: _n(inc.eps),
|
|
36
|
+
epsDiluted: _n(inc.epsdiluted)
|
|
37
|
+
},
|
|
38
|
+
balance: {
|
|
39
|
+
totalAssets: _n(bal.totalAssets),
|
|
40
|
+
currentAssets: _n(bal.totalCurrentAssets),
|
|
41
|
+
cash: _n(bal.cashAndCashEquivalents),
|
|
42
|
+
accountsReceivable: _n(bal.netReceivables),
|
|
43
|
+
inventory: _n(bal.inventory),
|
|
44
|
+
netPPE: _n(bal.propertyPlantEquipmentNet),
|
|
45
|
+
goodwill: _n(bal.goodwill),
|
|
46
|
+
intangibleAssets: _n(bal.intangibleAssets),
|
|
47
|
+
retainedEarnings: _n(bal.retainedEarnings),
|
|
48
|
+
totalLiabilities: _n(bal.totalLiabilities),
|
|
49
|
+
currentLiabilities: _n(bal.totalCurrentLiabilities),
|
|
50
|
+
accountsPayable: _n(bal.accountPayables),
|
|
51
|
+
longTermDebt: _n(bal.longTermDebt),
|
|
52
|
+
totalDebt: _n(bal.totalDebt),
|
|
53
|
+
totalEquity: _n(bal.totalStockholdersEquity),
|
|
54
|
+
sharesOutstanding: _n(inc.weightedAverageShsOut)
|
|
55
|
+
},
|
|
56
|
+
cashFlow: {
|
|
57
|
+
operatingCashFlow: _n(cf.operatingCashFlow),
|
|
58
|
+
capex: Math.abs(_n(cf.capitalExpenditure)),
|
|
59
|
+
investingCashFlow: _n(cf.investingActivitiesCashFlow),
|
|
60
|
+
financingCashFlow: _n(cf.financingActivitiesCashFlow),
|
|
61
|
+
dividendsPaid: _n(cf.commonDividendsPaid),
|
|
62
|
+
stockBasedCompensation: _n(cf.stockBasedCompensation),
|
|
63
|
+
debtRepayments: _n(cf.debtRepayment)
|
|
64
|
+
},
|
|
65
|
+
marketData: {
|
|
66
|
+
price: _n(profile.price),
|
|
67
|
+
marketCap: _n(profile.mktCap),
|
|
68
|
+
sharesOutstanding: _n(inc.weightedAverageShsOut),
|
|
69
|
+
ticker
|
|
70
|
+
},
|
|
71
|
+
ratios: {
|
|
72
|
+
peRatio: _n(rat.priceEarningsRatio),
|
|
73
|
+
pbRatio: _n(rat.priceToBookRatio),
|
|
74
|
+
psRatio: _n(rat.priceToSalesRatio),
|
|
75
|
+
evToEbitda: _n(rat.enterpriseValueMultiple),
|
|
76
|
+
roic: _n(rat.returnOnCapitalEmployed),
|
|
77
|
+
roe: _n(rat.returnOnEquity),
|
|
78
|
+
roa: _n(rat.returnOnAssets),
|
|
79
|
+
currentRatio: _n(rat.currentRatio),
|
|
80
|
+
debtToEquity: _n(rat.debtEquityRatio),
|
|
81
|
+
grossMargin: _n(rat.grossProfitMargin),
|
|
82
|
+
operatingMargin: _n(rat.operatingProfitMargin),
|
|
83
|
+
netMargin: _n(rat.netProfitMargin)
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
return results;
|
|
88
|
+
}
|
|
89
|
+
async function fetchFmpSp500(apiKey) {
|
|
90
|
+
const data = await _get(`${FMP_BASE}/sp500_constituent`, apiKey, {});
|
|
91
|
+
return data.map((d) => d.symbol).filter(Boolean);
|
|
92
|
+
}
|
|
93
|
+
async function _get(url, apiKey, params) {
|
|
94
|
+
const query = new URLSearchParams({ ...Object.fromEntries(Object.entries(params).map(([k, v]) => [k, String(v)])), apikey: apiKey });
|
|
95
|
+
const resp = await fetch(`${url}?${query}`);
|
|
96
|
+
if (!resp.ok) throw new Error(`FMP request failed: ${resp.status} ${url}`);
|
|
97
|
+
return resp.json();
|
|
98
|
+
}
|
|
99
|
+
function _n(v) {
|
|
100
|
+
if (v == null) return 0;
|
|
101
|
+
const n = Number(v);
|
|
102
|
+
return isNaN(n) ? 0 : n;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export { fetchFmp, fetchFmpSp500 };
|
|
106
|
+
//# sourceMappingURL=index.js.map
|
|
107
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/fetchers/fmp/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Financial Modeling Prep (FMP) fetcher.\n * Free tier: 250 requests/day.\n * Get API key: https://financialmodelingprep.com/developer/docs\n */\n\nimport type {\n IncomeStatement,\n BalanceSheet,\n CashFlowStatement,\n MarketData,\n} from '../../types/index.js'\n\nconst FMP_BASE = 'https://financialmodelingprep.com/api/v3'\n\nexport interface FmpOptions {\n apiKey: string\n periods?: number\n quarterly?: boolean\n}\n\nexport interface FmpPeriodData {\n period: string\n income: IncomeStatement\n balance: BalanceSheet\n cashFlow: CashFlowStatement\n marketData: MarketData\n ratios?: Record<string, number | null>\n}\n\n/**\n * Fetch financial statements from Financial Modeling Prep.\n *\n * @example\n * const data = await fetchFmp('AAPL', { apiKey: 'your_key', periods: 4 })\n * for (const period of data) {\n * const roicVal = roic({ nopat: ..., investedCapital: ... })\n * }\n */\nexport async function fetchFmp(\n ticker: string,\n options: FmpOptions\n): Promise<FmpPeriodData[]> {\n const { apiKey, periods = 4, quarterly = false } = options\n const period = quarterly ? 'quarter' : 'annual'\n const limit = periods\n\n const [incData, balData, cfData, ratioData, profileData] = await Promise.all([\n _get(`${FMP_BASE}/income-statement/${ticker}`, apiKey, { period, limit }),\n _get(`${FMP_BASE}/balance-sheet-statement/${ticker}`, apiKey, { period, limit }),\n _get(`${FMP_BASE}/cash-flow-statement/${ticker}`, apiKey, { period, limit }),\n _get(`${FMP_BASE}/ratios/${ticker}`, apiKey, { period, limit }),\n _get(`${FMP_BASE}/profile/${ticker}`, apiKey, {}),\n ])\n\n const profile = (profileData as any[])[0] ?? {}\n const results: FmpPeriodData[] = []\n\n for (let i = 0; i < Math.min(limit, (incData as any[]).length); i++) {\n const inc = (incData as any[])[i] ?? {}\n const bal = (balData as any[])[i] ?? {}\n const cf = (cfData as any[])[i] ?? {}\n const rat = (ratioData as any[])[i] ?? {}\n\n results.push({\n period: inc.date ?? '',\n income: {\n revenue: _n(inc.revenue),\n grossProfit: _n(inc.grossProfit),\n cogs: _n(inc.costOfRevenue),\n ebit: _n(inc.operatingIncome),\n ebitda: _n(inc.ebitda),\n netIncome: _n(inc.netIncome),\n interestExpense: Math.abs(_n(inc.interestExpense)),\n incomeTaxExpense: _n(inc.incomeTaxExpense),\n ebt: _n(inc.incomeBeforeTax),\n depreciationAndAmortization: _n(inc.depreciationAndAmortization),\n sharesOutstanding: _n(inc.weightedAverageShsOut),\n eps: _n(inc.eps),\n epsDiluted: _n(inc.epsdiluted),\n },\n balance: {\n totalAssets: _n(bal.totalAssets),\n currentAssets: _n(bal.totalCurrentAssets),\n cash: _n(bal.cashAndCashEquivalents),\n accountsReceivable: _n(bal.netReceivables),\n inventory: _n(bal.inventory),\n netPPE: _n(bal.propertyPlantEquipmentNet),\n goodwill: _n(bal.goodwill),\n intangibleAssets: _n(bal.intangibleAssets),\n retainedEarnings: _n(bal.retainedEarnings),\n totalLiabilities: _n(bal.totalLiabilities),\n currentLiabilities: _n(bal.totalCurrentLiabilities),\n accountsPayable: _n(bal.accountPayables),\n longTermDebt: _n(bal.longTermDebt),\n totalDebt: _n(bal.totalDebt),\n totalEquity: _n(bal.totalStockholdersEquity),\n sharesOutstanding: _n(inc.weightedAverageShsOut),\n },\n cashFlow: {\n operatingCashFlow: _n(cf.operatingCashFlow),\n capex: Math.abs(_n(cf.capitalExpenditure)),\n investingCashFlow: _n(cf.investingActivitiesCashFlow),\n financingCashFlow: _n(cf.financingActivitiesCashFlow),\n dividendsPaid: _n(cf.commonDividendsPaid),\n stockBasedCompensation: _n(cf.stockBasedCompensation),\n debtRepayments: _n(cf.debtRepayment),\n },\n marketData: {\n price: _n(profile.price),\n marketCap: _n(profile.mktCap),\n sharesOutstanding: _n(inc.weightedAverageShsOut),\n ticker,\n },\n ratios: {\n peRatio: _n(rat.priceEarningsRatio),\n pbRatio: _n(rat.priceToBookRatio),\n psRatio: _n(rat.priceToSalesRatio),\n evToEbitda: _n(rat.enterpriseValueMultiple),\n roic: _n(rat.returnOnCapitalEmployed),\n roe: _n(rat.returnOnEquity),\n roa: _n(rat.returnOnAssets),\n currentRatio: _n(rat.currentRatio),\n debtToEquity: _n(rat.debtEquityRatio),\n grossMargin: _n(rat.grossProfitMargin),\n operatingMargin: _n(rat.operatingProfitMargin),\n netMargin: _n(rat.netProfitMargin),\n },\n })\n }\n\n return results\n}\n\n/** Fetch the current S&P 500 constituent list. */\nexport async function fetchFmpSp500(apiKey: string): Promise<string[]> {\n const data = (await _get(`${FMP_BASE}/sp500_constituent`, apiKey, {})) as any[]\n return data.map(d => d.symbol).filter(Boolean)\n}\n\nasync function _get(url: string, apiKey: string, params: Record<string, unknown>): Promise<unknown> {\n const query = new URLSearchParams({ ...Object.fromEntries(Object.entries(params).map(([k, v]) => [k, String(v)])), apikey: apiKey })\n const resp = await fetch(`${url}?${query}`)\n if (!resp.ok) throw new Error(`FMP request failed: ${resp.status} ${url}`)\n return resp.json()\n}\n\nfunction _n(v: unknown): number {\n if (v == null) return 0\n const n = Number(v)\n return isNaN(n) ? 0 : n\n}\n"]}
|