fin-ratios 0.7.3 → 1.0.2
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/LICENSE +21 -0
- package/README.md +779 -0
- package/dist/fetchers/alphavantage/index.cjs +0 -2
- package/dist/fetchers/alphavantage/index.js +0 -2
- package/dist/fetchers/edgar/index.cjs +0 -2
- package/dist/fetchers/edgar/index.js +0 -2
- package/dist/fetchers/fmp/index.cjs +0 -2
- package/dist/fetchers/fmp/index.js +0 -2
- package/dist/fetchers/polygon/index.cjs +174 -0
- package/dist/fetchers/polygon/index.d.cts +84 -0
- package/dist/fetchers/polygon/index.d.ts +84 -0
- package/dist/fetchers/polygon/index.js +171 -0
- package/dist/fetchers/yahoo/index.cjs +0 -2
- package/dist/fetchers/yahoo/index.js +0 -2
- package/dist/hooks/index.cjs +0 -2
- package/dist/hooks/index.js +0 -2
- package/dist/index.cjs +557 -8
- package/dist/index.d.cts +289 -1
- package/dist/index.d.ts +289 -1
- package/dist/index.js +553 -9
- package/package.json +11 -3
- package/dist/fetchers/alphavantage/index.cjs.map +0 -1
- package/dist/fetchers/alphavantage/index.js.map +0 -1
- package/dist/fetchers/edgar/index.cjs.map +0 -1
- package/dist/fetchers/edgar/index.js.map +0 -1
- package/dist/fetchers/fmp/index.cjs.map +0 -1
- package/dist/fetchers/fmp/index.js.map +0 -1
- package/dist/fetchers/yahoo/index.cjs.map +0 -1
- package/dist/fetchers/yahoo/index.js.map +0 -1
- package/dist/hooks/index.cjs.map +0 -1
- package/dist/hooks/index.js.map +0 -1
- package/dist/index.cjs.map +0 -1
- package/dist/index.js.map +0 -1
|
@@ -148,5 +148,3 @@ exports.fetchEdgarFlat = fetchEdgarFlat;
|
|
|
148
148
|
exports.fetchEdgarNormalized = fetchEdgarNormalized;
|
|
149
149
|
exports.flattenEdgarData = flattenEdgarData;
|
|
150
150
|
exports.normalizeForScoring = normalizeForScoring;
|
|
151
|
-
//# sourceMappingURL=index.cjs.map
|
|
152
|
-
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// src/fetchers/polygon/index.ts
|
|
4
|
+
var POLYGON_BASE = "https://api.polygon.io/vX/reference/financials";
|
|
5
|
+
var _moduleKey;
|
|
6
|
+
function setPolygonApiKey(key) {
|
|
7
|
+
_moduleKey = key;
|
|
8
|
+
}
|
|
9
|
+
async function fetchPolygon(ticker, options = {}) {
|
|
10
|
+
const { numYears = 5, timeframe = "annual" } = options;
|
|
11
|
+
const apiKey = options.apiKey ?? _moduleKey ?? (typeof process !== "undefined" ? process.env["POLYGON_API_KEY"] : void 0);
|
|
12
|
+
if (!apiKey) {
|
|
13
|
+
throw new Error(
|
|
14
|
+
"Polygon.io API key is required. Provide it via options.apiKey, setPolygonApiKey(), or the POLYGON_API_KEY environment variable."
|
|
15
|
+
);
|
|
16
|
+
}
|
|
17
|
+
const startDate = _startDate(Math.max(numYears, 2));
|
|
18
|
+
const params = new URLSearchParams({
|
|
19
|
+
ticker: ticker.toUpperCase(),
|
|
20
|
+
timeframe,
|
|
21
|
+
limit: String(Math.min(numYears, 100)),
|
|
22
|
+
sort: "period_of_report_date",
|
|
23
|
+
order: "asc",
|
|
24
|
+
"filing_date.gte": startDate,
|
|
25
|
+
apiKey
|
|
26
|
+
});
|
|
27
|
+
const url = `${POLYGON_BASE}?${params.toString()}`;
|
|
28
|
+
let payload;
|
|
29
|
+
try {
|
|
30
|
+
const resp = await fetch(url);
|
|
31
|
+
if (resp.status === 401) {
|
|
32
|
+
throw new Error(
|
|
33
|
+
"Polygon API returned 401 Unauthorized \u2014 check your API key."
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
if (resp.status === 429) {
|
|
37
|
+
throw new Error(
|
|
38
|
+
"Polygon API rate limit exceeded (free tier: 5 req/min). Wait a moment and retry."
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
if (!resp.ok) {
|
|
42
|
+
const body = await resp.text().catch(() => "");
|
|
43
|
+
throw new Error(
|
|
44
|
+
`Polygon API request failed for ${ticker}: HTTP ${resp.status} \u2014 ${body.slice(0, 200)}`
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
payload = await resp.json();
|
|
48
|
+
} catch (err) {
|
|
49
|
+
if (err instanceof Error && err.message.startsWith("Polygon API")) throw err;
|
|
50
|
+
throw new Error(
|
|
51
|
+
`Polygon API request failed for ${ticker}: ${err instanceof Error ? err.message : String(err)}`
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
const results = payload.results ?? [];
|
|
55
|
+
return results.map((item) => _mapRecord(item));
|
|
56
|
+
}
|
|
57
|
+
function _mapRecord(item) {
|
|
58
|
+
const inc = item.financials?.income_statement ?? {};
|
|
59
|
+
const bal = item.financials?.balance_sheet ?? {};
|
|
60
|
+
const cf = item.financials?.cash_flow_statement ?? {};
|
|
61
|
+
const revenue = _v(inc, "revenues", "net_revenues", "total_revenues");
|
|
62
|
+
const grossProfit = _v(inc, "gross_profit");
|
|
63
|
+
const ebit = _v(
|
|
64
|
+
inc,
|
|
65
|
+
"operating_income",
|
|
66
|
+
"income_loss_from_continuing_operations_before_tax"
|
|
67
|
+
);
|
|
68
|
+
const netIncome = _v(inc, "net_income_loss", "net_income_loss_attributable_to_parent");
|
|
69
|
+
const taxExpense = _v(inc, "income_tax_expense_benefit");
|
|
70
|
+
const interestExp = Math.abs(_v(inc, "interest_expense_operating", "interest_expense"));
|
|
71
|
+
const ebtDirect = _v(
|
|
72
|
+
inc,
|
|
73
|
+
"income_loss_from_continuing_operations_before_tax",
|
|
74
|
+
"income_before_income_taxes"
|
|
75
|
+
);
|
|
76
|
+
const ebt = ebtDirect !== 0 ? ebtDirect : netIncome + taxExpense;
|
|
77
|
+
const totalAssets = _v(bal, "assets");
|
|
78
|
+
const currentAssets = _v(bal, "current_assets");
|
|
79
|
+
const cash = _v(
|
|
80
|
+
bal,
|
|
81
|
+
"cash_and_cash_equivalents_including_short_term_investments",
|
|
82
|
+
"cash_and_cash_equivalents",
|
|
83
|
+
"cash"
|
|
84
|
+
);
|
|
85
|
+
const currentLiab = _v(bal, "current_liabilities");
|
|
86
|
+
const totalEquity = _v(
|
|
87
|
+
bal,
|
|
88
|
+
"equity",
|
|
89
|
+
"stockholders_equity",
|
|
90
|
+
"equity_attributable_to_parent"
|
|
91
|
+
);
|
|
92
|
+
const longTermDebt = _v(bal, "long_term_debt");
|
|
93
|
+
const ar = _v(
|
|
94
|
+
bal,
|
|
95
|
+
"accounts_receivable",
|
|
96
|
+
"accounts_receivable_net_current",
|
|
97
|
+
"trade_and_other_receivables_current"
|
|
98
|
+
);
|
|
99
|
+
const ocf = _v(
|
|
100
|
+
cf,
|
|
101
|
+
"net_cash_flow_from_operating_activities",
|
|
102
|
+
"net_cash_provided_by_used_in_operating_activities"
|
|
103
|
+
);
|
|
104
|
+
const investingCf = _v(
|
|
105
|
+
cf,
|
|
106
|
+
"net_cash_flow_from_investing_activities",
|
|
107
|
+
"net_cash_provided_by_used_in_investing_activities"
|
|
108
|
+
);
|
|
109
|
+
const capexDirect = _v(
|
|
110
|
+
cf,
|
|
111
|
+
"payments_to_acquire_property_plant_and_equipment",
|
|
112
|
+
"capital_expenditures"
|
|
113
|
+
);
|
|
114
|
+
const capex = capexDirect !== 0 ? Math.abs(capexDirect) : Math.abs(investingCf);
|
|
115
|
+
const dividendsPaid = Math.abs(
|
|
116
|
+
_v(cf, "payments_of_dividends", "payments_of_dividends_common_stock", "dividends_paid")
|
|
117
|
+
);
|
|
118
|
+
let depreciation = _v(
|
|
119
|
+
inc,
|
|
120
|
+
"depreciation_and_amortization",
|
|
121
|
+
"depreciation_depletion_and_amortization"
|
|
122
|
+
);
|
|
123
|
+
if (depreciation === 0) {
|
|
124
|
+
depreciation = _v(
|
|
125
|
+
cf,
|
|
126
|
+
"depreciation_depletion_and_amortization",
|
|
127
|
+
"depreciation_and_amortization"
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
const sharesBS = _v(bal, "common_stock_shares_outstanding");
|
|
131
|
+
const sharesInc = _v(inc, "basic_average_shares", "diluted_average_shares");
|
|
132
|
+
const sharesOutstanding = sharesBS !== 0 ? sharesBS : sharesInc !== 0 ? sharesInc : void 0;
|
|
133
|
+
const year = String(item.fiscal_year ?? item.period_of_report_date?.slice(0, 4) ?? "");
|
|
134
|
+
return {
|
|
135
|
+
year,
|
|
136
|
+
revenue,
|
|
137
|
+
grossProfit,
|
|
138
|
+
ebit,
|
|
139
|
+
netIncome,
|
|
140
|
+
totalAssets,
|
|
141
|
+
totalEquity,
|
|
142
|
+
totalDebt: longTermDebt,
|
|
143
|
+
cash,
|
|
144
|
+
capex,
|
|
145
|
+
depreciation,
|
|
146
|
+
operatingCashFlow: ocf,
|
|
147
|
+
incomeTaxExpense: taxExpense,
|
|
148
|
+
ebt,
|
|
149
|
+
interestExpense: interestExp,
|
|
150
|
+
currentAssets,
|
|
151
|
+
currentLiabilities: currentLiab,
|
|
152
|
+
accountsReceivable: ar,
|
|
153
|
+
dividendsPaid,
|
|
154
|
+
...sharesOutstanding !== void 0 ? { sharesOutstanding } : {}
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
function _v(section, ...keys) {
|
|
158
|
+
for (const k of keys) {
|
|
159
|
+
const node = section[k];
|
|
160
|
+
if (node && node.value != null) {
|
|
161
|
+
const n = Number(node.value);
|
|
162
|
+
if (!isNaN(n)) return n;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return 0;
|
|
166
|
+
}
|
|
167
|
+
function _startDate(years) {
|
|
168
|
+
const d = /* @__PURE__ */ new Date();
|
|
169
|
+
d.setFullYear(d.getFullYear() - years);
|
|
170
|
+
return d.toISOString().slice(0, 10);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
exports.fetchPolygon = fetchPolygon;
|
|
174
|
+
exports.setPolygonApiKey = setPolygonApiKey;
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Polygon.io fetcher for fin-ratios (TypeScript).
|
|
3
|
+
*
|
|
4
|
+
* Fetches fundamental financial data from the Polygon.io REST API.
|
|
5
|
+
* API key required — get one at https://polygon.io.
|
|
6
|
+
* US companies only. Free tier: 5 req/min, ~2 years of history.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* import { fetchPolygon, setPolygonApiKey } from 'fin-ratios/fetchers/polygon'
|
|
10
|
+
* setPolygonApiKey('your-api-key')
|
|
11
|
+
* const data = await fetchPolygon('AAPL', { numYears: 5 })
|
|
12
|
+
* // data is oldest-first and compatible with all scoring utilities
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* Store a Polygon.io API key module-wide.
|
|
16
|
+
* Subsequent calls to {@link fetchPolygon} will use this key unless overridden
|
|
17
|
+
* via the {@link PolygonOptions.apiKey} option.
|
|
18
|
+
*/
|
|
19
|
+
declare function setPolygonApiKey(key: string): void;
|
|
20
|
+
interface PolygonOptions {
|
|
21
|
+
/** Number of annual periods to retrieve (default: 5). */
|
|
22
|
+
numYears?: number;
|
|
23
|
+
/**
|
|
24
|
+
* Polygon.io API key. Falls back to the module-level key set via
|
|
25
|
+
* {@link setPolygonApiKey} and then to the `POLYGON_API_KEY` environment
|
|
26
|
+
* variable (Node.js only).
|
|
27
|
+
*/
|
|
28
|
+
apiKey?: string;
|
|
29
|
+
/** ``'annual'`` (default) or ``'quarterly'``. */
|
|
30
|
+
timeframe?: 'annual' | 'quarterly';
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Normalised annual financial record returned by {@link fetchPolygon}.
|
|
34
|
+
* Field names are compatible with the `EdgarAnnualRecord` type used by the
|
|
35
|
+
* EDGAR fetcher, so the data can be passed directly to all scoring utilities:
|
|
36
|
+
* `moatScore`, `capitalAllocationScore`, `earningsQualityScore`,
|
|
37
|
+
* `qualityScore`, `investmentScore`, etc.
|
|
38
|
+
*/
|
|
39
|
+
interface PolygonAnnualRecord {
|
|
40
|
+
/** Four-digit fiscal year string, e.g. ``'2023'``. */
|
|
41
|
+
year: string;
|
|
42
|
+
revenue: number;
|
|
43
|
+
grossProfit: number;
|
|
44
|
+
/** Operating income (EBIT). */
|
|
45
|
+
ebit: number;
|
|
46
|
+
netIncome: number;
|
|
47
|
+
totalAssets: number;
|
|
48
|
+
totalEquity: number;
|
|
49
|
+
/** Long-term debt (used as total debt proxy). */
|
|
50
|
+
totalDebt: number;
|
|
51
|
+
cash: number;
|
|
52
|
+
/** Capital expenditures (absolute value). */
|
|
53
|
+
capex: number;
|
|
54
|
+
depreciation: number;
|
|
55
|
+
operatingCashFlow: number;
|
|
56
|
+
incomeTaxExpense: number;
|
|
57
|
+
ebt: number;
|
|
58
|
+
interestExpense: number;
|
|
59
|
+
currentAssets: number;
|
|
60
|
+
currentLiabilities: number;
|
|
61
|
+
accountsReceivable: number;
|
|
62
|
+
/** Dividends paid (absolute value). Zero for non-payers. */
|
|
63
|
+
dividendsPaid?: number;
|
|
64
|
+
/** Shares outstanding (or weighted-average basic shares). */
|
|
65
|
+
sharesOutstanding?: number;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Fetch annual financial data from Polygon.io.
|
|
69
|
+
*
|
|
70
|
+
* @param ticker - Stock ticker symbol (e.g. ``'AAPL'``).
|
|
71
|
+
* @param options - Options including API key, number of years, and timeframe.
|
|
72
|
+
* @returns Oldest-first list of annual records compatible with all scoring
|
|
73
|
+
* utilities (moatScore, capitalAllocationScore, qualityScore, etc.).
|
|
74
|
+
*
|
|
75
|
+
* @throws {Error} If no API key is available.
|
|
76
|
+
* @throws {Error} If the Polygon.io API request fails.
|
|
77
|
+
*
|
|
78
|
+
* @example
|
|
79
|
+
* import { fetchPolygon } from 'fin-ratios/fetchers/polygon'
|
|
80
|
+
* const data = await fetchPolygon('AAPL', { numYears: 5, apiKey: 'your-key' })
|
|
81
|
+
*/
|
|
82
|
+
declare function fetchPolygon(ticker: string, options?: PolygonOptions): Promise<PolygonAnnualRecord[]>;
|
|
83
|
+
|
|
84
|
+
export { type PolygonAnnualRecord, type PolygonOptions, fetchPolygon, setPolygonApiKey };
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Polygon.io fetcher for fin-ratios (TypeScript).
|
|
3
|
+
*
|
|
4
|
+
* Fetches fundamental financial data from the Polygon.io REST API.
|
|
5
|
+
* API key required — get one at https://polygon.io.
|
|
6
|
+
* US companies only. Free tier: 5 req/min, ~2 years of history.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* import { fetchPolygon, setPolygonApiKey } from 'fin-ratios/fetchers/polygon'
|
|
10
|
+
* setPolygonApiKey('your-api-key')
|
|
11
|
+
* const data = await fetchPolygon('AAPL', { numYears: 5 })
|
|
12
|
+
* // data is oldest-first and compatible with all scoring utilities
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* Store a Polygon.io API key module-wide.
|
|
16
|
+
* Subsequent calls to {@link fetchPolygon} will use this key unless overridden
|
|
17
|
+
* via the {@link PolygonOptions.apiKey} option.
|
|
18
|
+
*/
|
|
19
|
+
declare function setPolygonApiKey(key: string): void;
|
|
20
|
+
interface PolygonOptions {
|
|
21
|
+
/** Number of annual periods to retrieve (default: 5). */
|
|
22
|
+
numYears?: number;
|
|
23
|
+
/**
|
|
24
|
+
* Polygon.io API key. Falls back to the module-level key set via
|
|
25
|
+
* {@link setPolygonApiKey} and then to the `POLYGON_API_KEY` environment
|
|
26
|
+
* variable (Node.js only).
|
|
27
|
+
*/
|
|
28
|
+
apiKey?: string;
|
|
29
|
+
/** ``'annual'`` (default) or ``'quarterly'``. */
|
|
30
|
+
timeframe?: 'annual' | 'quarterly';
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Normalised annual financial record returned by {@link fetchPolygon}.
|
|
34
|
+
* Field names are compatible with the `EdgarAnnualRecord` type used by the
|
|
35
|
+
* EDGAR fetcher, so the data can be passed directly to all scoring utilities:
|
|
36
|
+
* `moatScore`, `capitalAllocationScore`, `earningsQualityScore`,
|
|
37
|
+
* `qualityScore`, `investmentScore`, etc.
|
|
38
|
+
*/
|
|
39
|
+
interface PolygonAnnualRecord {
|
|
40
|
+
/** Four-digit fiscal year string, e.g. ``'2023'``. */
|
|
41
|
+
year: string;
|
|
42
|
+
revenue: number;
|
|
43
|
+
grossProfit: number;
|
|
44
|
+
/** Operating income (EBIT). */
|
|
45
|
+
ebit: number;
|
|
46
|
+
netIncome: number;
|
|
47
|
+
totalAssets: number;
|
|
48
|
+
totalEquity: number;
|
|
49
|
+
/** Long-term debt (used as total debt proxy). */
|
|
50
|
+
totalDebt: number;
|
|
51
|
+
cash: number;
|
|
52
|
+
/** Capital expenditures (absolute value). */
|
|
53
|
+
capex: number;
|
|
54
|
+
depreciation: number;
|
|
55
|
+
operatingCashFlow: number;
|
|
56
|
+
incomeTaxExpense: number;
|
|
57
|
+
ebt: number;
|
|
58
|
+
interestExpense: number;
|
|
59
|
+
currentAssets: number;
|
|
60
|
+
currentLiabilities: number;
|
|
61
|
+
accountsReceivable: number;
|
|
62
|
+
/** Dividends paid (absolute value). Zero for non-payers. */
|
|
63
|
+
dividendsPaid?: number;
|
|
64
|
+
/** Shares outstanding (or weighted-average basic shares). */
|
|
65
|
+
sharesOutstanding?: number;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Fetch annual financial data from Polygon.io.
|
|
69
|
+
*
|
|
70
|
+
* @param ticker - Stock ticker symbol (e.g. ``'AAPL'``).
|
|
71
|
+
* @param options - Options including API key, number of years, and timeframe.
|
|
72
|
+
* @returns Oldest-first list of annual records compatible with all scoring
|
|
73
|
+
* utilities (moatScore, capitalAllocationScore, qualityScore, etc.).
|
|
74
|
+
*
|
|
75
|
+
* @throws {Error} If no API key is available.
|
|
76
|
+
* @throws {Error} If the Polygon.io API request fails.
|
|
77
|
+
*
|
|
78
|
+
* @example
|
|
79
|
+
* import { fetchPolygon } from 'fin-ratios/fetchers/polygon'
|
|
80
|
+
* const data = await fetchPolygon('AAPL', { numYears: 5, apiKey: 'your-key' })
|
|
81
|
+
*/
|
|
82
|
+
declare function fetchPolygon(ticker: string, options?: PolygonOptions): Promise<PolygonAnnualRecord[]>;
|
|
83
|
+
|
|
84
|
+
export { type PolygonAnnualRecord, type PolygonOptions, fetchPolygon, setPolygonApiKey };
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
// src/fetchers/polygon/index.ts
|
|
2
|
+
var POLYGON_BASE = "https://api.polygon.io/vX/reference/financials";
|
|
3
|
+
var _moduleKey;
|
|
4
|
+
function setPolygonApiKey(key) {
|
|
5
|
+
_moduleKey = key;
|
|
6
|
+
}
|
|
7
|
+
async function fetchPolygon(ticker, options = {}) {
|
|
8
|
+
const { numYears = 5, timeframe = "annual" } = options;
|
|
9
|
+
const apiKey = options.apiKey ?? _moduleKey ?? (typeof process !== "undefined" ? process.env["POLYGON_API_KEY"] : void 0);
|
|
10
|
+
if (!apiKey) {
|
|
11
|
+
throw new Error(
|
|
12
|
+
"Polygon.io API key is required. Provide it via options.apiKey, setPolygonApiKey(), or the POLYGON_API_KEY environment variable."
|
|
13
|
+
);
|
|
14
|
+
}
|
|
15
|
+
const startDate = _startDate(Math.max(numYears, 2));
|
|
16
|
+
const params = new URLSearchParams({
|
|
17
|
+
ticker: ticker.toUpperCase(),
|
|
18
|
+
timeframe,
|
|
19
|
+
limit: String(Math.min(numYears, 100)),
|
|
20
|
+
sort: "period_of_report_date",
|
|
21
|
+
order: "asc",
|
|
22
|
+
"filing_date.gte": startDate,
|
|
23
|
+
apiKey
|
|
24
|
+
});
|
|
25
|
+
const url = `${POLYGON_BASE}?${params.toString()}`;
|
|
26
|
+
let payload;
|
|
27
|
+
try {
|
|
28
|
+
const resp = await fetch(url);
|
|
29
|
+
if (resp.status === 401) {
|
|
30
|
+
throw new Error(
|
|
31
|
+
"Polygon API returned 401 Unauthorized \u2014 check your API key."
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
if (resp.status === 429) {
|
|
35
|
+
throw new Error(
|
|
36
|
+
"Polygon API rate limit exceeded (free tier: 5 req/min). Wait a moment and retry."
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
if (!resp.ok) {
|
|
40
|
+
const body = await resp.text().catch(() => "");
|
|
41
|
+
throw new Error(
|
|
42
|
+
`Polygon API request failed for ${ticker}: HTTP ${resp.status} \u2014 ${body.slice(0, 200)}`
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
payload = await resp.json();
|
|
46
|
+
} catch (err) {
|
|
47
|
+
if (err instanceof Error && err.message.startsWith("Polygon API")) throw err;
|
|
48
|
+
throw new Error(
|
|
49
|
+
`Polygon API request failed for ${ticker}: ${err instanceof Error ? err.message : String(err)}`
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
const results = payload.results ?? [];
|
|
53
|
+
return results.map((item) => _mapRecord(item));
|
|
54
|
+
}
|
|
55
|
+
function _mapRecord(item) {
|
|
56
|
+
const inc = item.financials?.income_statement ?? {};
|
|
57
|
+
const bal = item.financials?.balance_sheet ?? {};
|
|
58
|
+
const cf = item.financials?.cash_flow_statement ?? {};
|
|
59
|
+
const revenue = _v(inc, "revenues", "net_revenues", "total_revenues");
|
|
60
|
+
const grossProfit = _v(inc, "gross_profit");
|
|
61
|
+
const ebit = _v(
|
|
62
|
+
inc,
|
|
63
|
+
"operating_income",
|
|
64
|
+
"income_loss_from_continuing_operations_before_tax"
|
|
65
|
+
);
|
|
66
|
+
const netIncome = _v(inc, "net_income_loss", "net_income_loss_attributable_to_parent");
|
|
67
|
+
const taxExpense = _v(inc, "income_tax_expense_benefit");
|
|
68
|
+
const interestExp = Math.abs(_v(inc, "interest_expense_operating", "interest_expense"));
|
|
69
|
+
const ebtDirect = _v(
|
|
70
|
+
inc,
|
|
71
|
+
"income_loss_from_continuing_operations_before_tax",
|
|
72
|
+
"income_before_income_taxes"
|
|
73
|
+
);
|
|
74
|
+
const ebt = ebtDirect !== 0 ? ebtDirect : netIncome + taxExpense;
|
|
75
|
+
const totalAssets = _v(bal, "assets");
|
|
76
|
+
const currentAssets = _v(bal, "current_assets");
|
|
77
|
+
const cash = _v(
|
|
78
|
+
bal,
|
|
79
|
+
"cash_and_cash_equivalents_including_short_term_investments",
|
|
80
|
+
"cash_and_cash_equivalents",
|
|
81
|
+
"cash"
|
|
82
|
+
);
|
|
83
|
+
const currentLiab = _v(bal, "current_liabilities");
|
|
84
|
+
const totalEquity = _v(
|
|
85
|
+
bal,
|
|
86
|
+
"equity",
|
|
87
|
+
"stockholders_equity",
|
|
88
|
+
"equity_attributable_to_parent"
|
|
89
|
+
);
|
|
90
|
+
const longTermDebt = _v(bal, "long_term_debt");
|
|
91
|
+
const ar = _v(
|
|
92
|
+
bal,
|
|
93
|
+
"accounts_receivable",
|
|
94
|
+
"accounts_receivable_net_current",
|
|
95
|
+
"trade_and_other_receivables_current"
|
|
96
|
+
);
|
|
97
|
+
const ocf = _v(
|
|
98
|
+
cf,
|
|
99
|
+
"net_cash_flow_from_operating_activities",
|
|
100
|
+
"net_cash_provided_by_used_in_operating_activities"
|
|
101
|
+
);
|
|
102
|
+
const investingCf = _v(
|
|
103
|
+
cf,
|
|
104
|
+
"net_cash_flow_from_investing_activities",
|
|
105
|
+
"net_cash_provided_by_used_in_investing_activities"
|
|
106
|
+
);
|
|
107
|
+
const capexDirect = _v(
|
|
108
|
+
cf,
|
|
109
|
+
"payments_to_acquire_property_plant_and_equipment",
|
|
110
|
+
"capital_expenditures"
|
|
111
|
+
);
|
|
112
|
+
const capex = capexDirect !== 0 ? Math.abs(capexDirect) : Math.abs(investingCf);
|
|
113
|
+
const dividendsPaid = Math.abs(
|
|
114
|
+
_v(cf, "payments_of_dividends", "payments_of_dividends_common_stock", "dividends_paid")
|
|
115
|
+
);
|
|
116
|
+
let depreciation = _v(
|
|
117
|
+
inc,
|
|
118
|
+
"depreciation_and_amortization",
|
|
119
|
+
"depreciation_depletion_and_amortization"
|
|
120
|
+
);
|
|
121
|
+
if (depreciation === 0) {
|
|
122
|
+
depreciation = _v(
|
|
123
|
+
cf,
|
|
124
|
+
"depreciation_depletion_and_amortization",
|
|
125
|
+
"depreciation_and_amortization"
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
const sharesBS = _v(bal, "common_stock_shares_outstanding");
|
|
129
|
+
const sharesInc = _v(inc, "basic_average_shares", "diluted_average_shares");
|
|
130
|
+
const sharesOutstanding = sharesBS !== 0 ? sharesBS : sharesInc !== 0 ? sharesInc : void 0;
|
|
131
|
+
const year = String(item.fiscal_year ?? item.period_of_report_date?.slice(0, 4) ?? "");
|
|
132
|
+
return {
|
|
133
|
+
year,
|
|
134
|
+
revenue,
|
|
135
|
+
grossProfit,
|
|
136
|
+
ebit,
|
|
137
|
+
netIncome,
|
|
138
|
+
totalAssets,
|
|
139
|
+
totalEquity,
|
|
140
|
+
totalDebt: longTermDebt,
|
|
141
|
+
cash,
|
|
142
|
+
capex,
|
|
143
|
+
depreciation,
|
|
144
|
+
operatingCashFlow: ocf,
|
|
145
|
+
incomeTaxExpense: taxExpense,
|
|
146
|
+
ebt,
|
|
147
|
+
interestExpense: interestExp,
|
|
148
|
+
currentAssets,
|
|
149
|
+
currentLiabilities: currentLiab,
|
|
150
|
+
accountsReceivable: ar,
|
|
151
|
+
dividendsPaid,
|
|
152
|
+
...sharesOutstanding !== void 0 ? { sharesOutstanding } : {}
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
function _v(section, ...keys) {
|
|
156
|
+
for (const k of keys) {
|
|
157
|
+
const node = section[k];
|
|
158
|
+
if (node && node.value != null) {
|
|
159
|
+
const n = Number(node.value);
|
|
160
|
+
if (!isNaN(n)) return n;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
return 0;
|
|
164
|
+
}
|
|
165
|
+
function _startDate(years) {
|
|
166
|
+
const d = /* @__PURE__ */ new Date();
|
|
167
|
+
d.setFullYear(d.getFullYear() - years);
|
|
168
|
+
return d.toISOString().slice(0, 10);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
export { fetchPolygon, setPolygonApiKey };
|
package/dist/hooks/index.cjs
CHANGED