UNPKG

yahoo-finance2

Version:
358 lines (357 loc) 13.6 kB
"use strict"; /** * Fundamentals Time Series module for retrieving detailed financial statement data over time. * * This module provides comprehensive time-series financial data including balance sheet, * income statement, and cash flow statement items across quarterly, annual, and trailing * periods. Essential for financial analysis, trend analysis, and fundamental research. * * @example Basic Usage - Balance Sheet Data * ```typescript * import YahooFinance from "yahoo-finance2"; * const yahooFinance = new YahooFinance(); * * // Get quarterly balance sheet data for the last 2 years * const balanceSheetData = await yahooFinance.fundamentalsTimeSeries('AAPL', { * period1: '2022-01-01', * period2: '2024-01-01', * type: 'quarterly', * module: 'balance-sheet' * }); * * // Access specific metrics * balanceSheetData.forEach(quarter => { * console.log(`${quarter.date}: Cash = $${quarter.quarterlyCashAndCashEquivalents}`); * console.log(`Total Assets = $${quarter.quarterlyTotalAssets}`); * }); * ``` * * @example Financials (Income Statement) Analysis * ```typescript * // Get annual income statement data * const financialsData = await yahooFinance.fundamentalsTimeSeries('MSFT', { * period1: '2020-01-01', * type: 'annual', * module: 'financials' * }); * * // Track revenue growth over years * financialsData.forEach(year => { * console.log(`${year.date.getFullYear()}: Revenue = $${year.annualTotalRevenue}`); * console.log(`Net Income = $${year.annualNetIncome}`); * }); * * // Calculate revenue growth rate * if (financialsData.length >= 2) { * const latest = financialsData[financialsData.length - 1]; * const previous = financialsData[financialsData.length - 2]; * const growthRate = ((latest.annualTotalRevenue - previous.annualTotalRevenue) / previous.annualTotalRevenue) * 100; * console.log(`Revenue growth: ${growthRate.toFixed(2)}%`); * } * ``` * * @example Cash Flow Analysis * ```typescript * // Get cash flow data for operational analysis * const cashFlowData = await yahooFinance.fundamentalsTimeSeries('GOOGL', { * period1: '2022-01-01', * type: 'quarterly', * module: 'cash-flow' * }); * * // Analyze cash generation trends * cashFlowData.forEach(quarter => { * const operatingCF = quarter.quarterlyOperatingCashFlow; * const freeCF = quarter.quarterlyFreeCashFlow; * const capex = quarter.quarterlyCapitalExpenditure; * * console.log(`${quarter.date}: Operating CF = $${operatingCF}`); * console.log(`Free CF = $${freeCF}, CapEx = $${capex}`); * }); * ``` * * @example Comprehensive Financial Analysis * ```typescript * // Get all financial data for complete analysis * const allData = await yahooFinance.fundamentalsTimeSeries('NVDA', { * period1: '2023-01-01', * type: 'quarterly', * module: 'all' * }); * * // Financial health analysis * allData.forEach(quarter => { * const totalAssets = quarter.quarterlyTotalAssets; * const totalDebt = quarter.quarterlyTotalDebt; * const cash = quarter.quarterlyCashAndCashEquivalents; * const revenue = quarter.quarterlyTotalRevenue; * * // Key ratios * const debtToAssets = (totalDebt / totalAssets) * 100; * const cashRatio = (cash / totalDebt) * 100; * * console.log(`${quarter.date}:`); * console.log(` Debt-to-Assets: ${debtToAssets.toFixed(2)}%`); * console.log(` Cash Coverage: ${cashRatio.toFixed(2)}%`); * }); * ``` * * @example Trailing Twelve Months (TTM) Data * ```typescript * // Get trailing 12-month data for current analysis * const ttmData = await yahooFinance.fundamentalsTimeSeries('TSLA', { * period1: '2023-01-01', * type: 'trailing', * module: 'financials' * }); * * // Get the most recent TTM figures * const latest = ttmData[ttmData.length - 1]; * console.log(`TTM Revenue: $${latest.trailingTotalRevenue}`); * console.log(`TTM Net Income: $${latest.trailingNetIncome}`); * ``` * * @example Comparative Analysis * ```typescript * // Compare multiple companies * const symbols = ['AAPL', 'MSFT', 'GOOGL']; * const comparisons = await Promise.all( * symbols.map(symbol => * yahooFinance.fundamentalsTimeSeries(symbol, { * period1: '2023-01-01', * type: 'annual', * module: 'financials' * }) * ) * ); * * // Compare latest year revenues * symbols.forEach((symbol, index) => { * const data = comparisons[index]; * const latestYear = data[data.length - 1]; * console.log(`${symbol}: $${latestYear.annualTotalRevenue} revenue`); * }); * ``` * * @example Error Handling and Data Validation * ```typescript * try { * const data = await yahooFinance.fundamentalsTimeSeries('AAPL', { * period1: '2020-01-01', * type: 'quarterly', * module: 'balance-sheet' * }); * * // Check for data availability * if (data.length === 0) { * console.log('No financial data available for the specified period'); * return; * } * * // Validate data completeness * data.forEach(quarter => { * if (!quarter.quarterlyTotalAssets) { * console.warn(`Missing total assets data for ${quarter.date}`); * } * }); * * } catch (error) { * console.error('Failed to fetch fundamentals data:', error.message); * } * ``` * * @remarks * **Data Types**: The module supports three reporting periods: * - `quarterly`: Individual quarter reporting periods * - `annual`: Full fiscal year data * - `trailing`: Rolling 12-month (TTM) data * * **Module Types**: Financial statement categories: * - `financials`: Income statement items (revenue, expenses, net income) * - `balance-sheet`: Balance sheet items (assets, liabilities, equity) * - `cash-flow`: Cash flow statement items (operating, investing, financing) * - `all`: Complete financial data from all statements * * **Data Availability**: Historical depth varies by company and may be limited * for newer public companies. Large cap stocks typically have 5+ years of data. * * **Currency**: All values are in the company's reporting currency (usually USD * for US companies). Values are typically in absolute amounts, not per-share. * * **Timing**: Data is typically available 1-2 days after earnings release. * Trailing data is updated quarterly with the latest reported period. * * @see {@link quoteSummary} for current period fundamental data * * @module fundamentalsTimeSeries */ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.FundamentalsTimeSeries_Modules = exports.FundamentalsTimeSeries_Types = void 0; exports.default = fundamentalsTimeSeries; const timeseries_js_1 = __importDefault(require("../lib/timeseries.js")); const index_js_1 = require("../lib/validate/index.js"); // @yf-schema: see the docs on how this file is automatically updated. const fundamentalsTimeSeries_schema_js_1 = __importDefault(require("./fundamentalsTimeSeries.schema.js")); const definitions = (0, index_js_1.getTypedDefinitions)(fundamentalsTimeSeries_schema_js_1.default); exports.FundamentalsTimeSeries_Types = ["quarterly", "annual", "trailing"]; exports.FundamentalsTimeSeries_Modules = [ "financials", "balance-sheet", "cash-flow", "all", ]; const queryOptionsDefaults = { merge: false, padTimeSeries: true, lang: "en-US", region: "US", type: "quarterly", }; function fundamentalsTimeSeries(symbol, queryOptionsOverrides, moduleOptions) { return this._moduleExec({ moduleName: "options", query: { assertSymbol: symbol, url: `https://query1.finance.yahoo.com/ws/fundamentals-timeseries/v1/finance/timeseries/${symbol}`, needsCrumb: false, definitions, schemaKey: "#/definitions/FundamentalsTimeSeriesOptions", defaults: queryOptionsDefaults, overrides: queryOptionsOverrides, transformWith: processQuery, }, result: { definitions, schemaKey: "#/definitions/FundamentalsTimeSeriesResults", // deno-lint-ignore no-explicit-any transformWith(response) { if (!response || !response.timeseries) { throw new Error(`Unexpected result: ${JSON.stringify(response)}`); } return processResponse(response, queryOptionsOverrides.module ?? queryOptionsOverrides.type); }, }, moduleOptions, }); } /** * Transform the input options into query parameters. * The options module defines which keys that are used in the query. * The keys are joined together into the query parameter type and * pre-fixed with the options type (e.g. annualTotalRevenue). * @param queryOptions Input query options. * @returns Query parameters. */ const processQuery = function (queryOptions) { // Convert dates if (!queryOptions.period2) queryOptions.period2 = new Date(); const dates = ["period1", "period2"]; for (const fieldName of dates) { const value = queryOptions[fieldName]; if (value instanceof Date) { queryOptions[fieldName] = Math.floor(value.getTime() / 1000); } else if (typeof value === "string") { const timestamp = new Date(value).getTime(); if (isNaN(timestamp)) { throw new Error("yahooFinance.fundamentalsTimeSeries() option '" + fieldName + "' invalid date provided: '" + value + "'"); } queryOptions[fieldName] = Math.floor(timestamp / 1000); } } // Validate query parameters. if (queryOptions.period1 === queryOptions.period2) { throw new Error("yahooFinance.fundamentalsTimeSeries() options `period1` and `period2` " + "cannot share the same value."); } else if (!exports.FundamentalsTimeSeries_Types.includes(queryOptions.type || "")) { throw new Error("yahooFinance.fundamentalsTimeSeries() option type invalid."); } else if (!exports.FundamentalsTimeSeries_Modules.includes(queryOptions.module || "")) { throw new Error("yahooFinance.fundamentalsTimeSeries() option module invalid."); } // Join the keys for the module into query types. const keys = Object.entries(timeseries_js_1.default).reduce((previous, [module, keys]) => { if (queryOptions.module == "all") { return previous.concat(keys); } else if (module == queryOptions.module) { return previous.concat(keys); } else return previous; }, []); const queryType = queryOptions.type + keys.join(`,${queryOptions.type}`); return { period1: queryOptions.period1, period2: queryOptions.period2, type: queryType, }; }; /** * Transforms the time-series into an array with reported values per period. * Each object represents a period and its properties are the data points. * Financial statement content variates and keys are skipped when empty. * The query keys include the option type (e.g. annualTotalRevenue). * In the response the type is removed (e.g. totalRevenue) for * easier mapping by the client. * @param response Query response. * @returns Formatted response. */ const processResponse = function ( // deno-lint-ignore no-explicit-any response, requestModule) { // deno-lint-ignore no-explicit-any const keyedByTimestamp = {}; const replace = new RegExp(exports.FundamentalsTimeSeries_Types.join("|")); for (let ct = 0; ct < response.timeseries.result.length; ct++) { let periodType = "UNKNOWN"; const result = response.timeseries.result[ct]; if (!result.timestamp || !result.timestamp.length) { continue; } for (let ct = 0; ct < result.timestamp.length; ct++) { const timestamp = result.timestamp[ct]; const dataKey = Object.keys(result)[2]; if (!keyedByTimestamp[timestamp]) { keyedByTimestamp[timestamp] = { date: timestamp }; } if (!result[dataKey][ct] || !result[dataKey][ct].reportedValue || !result[dataKey][ct].reportedValue.raw) { continue; } const short = dataKey.replace(replace, ""); const key = short == short.toUpperCase() ? short : short[0].toLowerCase() + short.slice(1); keyedByTimestamp[timestamp][key] = result[dataKey][ct].reportedValue.raw; const thisPeriodType = result[dataKey][ct].periodType; if (thisPeriodType) { if (periodType !== "UNKNOWN" && periodType !== thisPeriodType) { throw new Error("periodType mismatch - please report " + periodType + " " + thisPeriodType); } periodType = thisPeriodType; keyedByTimestamp[timestamp].periodType = periodType; } else { console.log("missing periodType", keyedByTimestamp[timestamp]); } } } return Object.values(keyedByTimestamp).filter((entry) => Object.keys(entry).length > 1).map((entry) => ({ TYPE: requestModule === "all" ? "ALL" : requestModule.toUpperCase().replace("-", "_"), ...entry, })); };