UNPKG

yahoo-finance2

Version:
295 lines (294 loc) 10.7 kB
"use strict"; /** * Chart module for retrieving historical price data with extensive customization options. * * This module provides detailed historical price, volume, and event data for financial * instruments, with flexible date ranges, intervals, and data formats. It's the primary * source for building charts and conducting technical analysis. * * @example Basic Usage * ```typescript * import YahooFinance from "yahoo-finance2"; * const yahooFinance = new YahooFinance(); * * // Get last year of daily data * const result = await yahooFinance.chart('AAPL', { * period1: '2023-01-01', * period2: '2024-01-01' * }); * * console.log(result.quotes[0]); // First day's OHLCV data * console.log(result.meta.currency); // "USD" * ``` * * @example Different Time Intervals * ```typescript * // Intraday 5-minute data for last 5 days * const intraday = await yahooFinance.chart('TSLA', { * period1: new Date(Date.now() - 5 * 24 * 60 * 60 * 1000), * interval: '5m' * }); * * // Weekly data for 2 years * const weekly = await yahooFinance.chart('NVDA', { * period1: '2022-01-01', * period2: '2024-01-01', * interval: '1wk' * }); * * // Monthly data for maximum available period * const monthly = await yahooFinance.chart('MSFT', { * period1: new Date('1985-01-01'), * interval: '1mo' * }); * ``` * * @example Object Format for Charting Libraries * ```typescript * // Get data in object format (better for some charting libraries) * const chartData = await yahooFinance.chart('GOOGL', { * period1: '2023-06-01', * period2: '2023-12-01', * return: 'object' * }); * * // Access data by timestamp keys * const timestamps = chartData.timestamp; * const prices = chartData.indicators.quote[0]; * * timestamps.forEach((timestamp, index) => { * console.log(`${new Date(timestamp * 1000)}: $${prices.close[index]}`); * }); * ``` * * @example Working with Events and Dividends * ```typescript * const data = await yahooFinance.chart('AAPL', { * period1: '2020-01-01', * period2: '2024-01-01', * events: 'div|split|earn' * }); * * // Process dividends * if (data.events?.dividends) { * data.events.dividends.forEach(dividend => { * console.log(`Dividend: $${dividend.amount} on ${dividend.date}`); * }); * } * * // Process stock splits * if (data.events?.splits) { * data.events.splits.forEach(split => { * console.log(`Split: ${split.splitRatio} on ${split.date}`); * }); * } * ``` * * @example Cryptocurrency and International Markets * ```typescript * // Bitcoin data * const btc = await yahooFinance.chart('BTC-USD', { * period1: '2024-01-01', * interval: '1h' * }); * * // European stocks * const asml = await yahooFinance.chart('ASML.AS', { * period1: '2023-01-01' * }); * * // Japanese stocks * const toyota = await yahooFinance.chart('7203.T', { * period1: '2023-01-01' * }); * ``` * * @example Error Handling and Edge Cases * ```typescript * try { * const result = await yahooFinance.chart('INVALID', { * period1: '2023-01-01' * }); * } catch (error) { * if (error.message.includes('No data found')) { * console.log('Symbol may be delisted or invalid'); * } * } * * // Handle potential empty results * const result = await yahooFinance.chart('AAPL', { * period1: '2025-01-01' // Future date * }); * * if (result.quotes.length === 0) { * console.log('No data available for the specified period'); * } * ``` * * @remarks * **Data Formats**: The module provides two return formats: * - `array` (default): Easier to iterate over with Date objects * - `object`: Optimized for charting libraries, with timestamp keys * * **Intervals**: Intraday intervals (1m, 5m, etc.) have limited historical availability * (~60 days for 1-minute data). Daily and longer intervals have extensive history. * * **Market Hours**: Pre/post market data is included by default but may not be * available for all symbols or time periods. * * **Delisted Stocks**: When a stock is delisted, ALL historical data becomes * unavailable through Yahoo Finance, including periods when it was actively traded. * * @see {@link historical} for a simpler interface focused on OHLCV data * * @module chart */ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports._chart = void 0; exports.default = chart; const index_js_1 = require("../lib/validate/index.js"); // @yf-schema: see the docs on how this file is automatically updated. const chart_schema_js_1 = __importDefault(require("./chart.schema.js")); const definitions = (0, index_js_1.getTypedDefinitions)(chart_schema_js_1.default); const queryOptionsDefaults = { useYfid: true, interval: "1d", includePrePost: true, events: "div|split|earn", lang: "en-US", return: "array", }; /* --- array input, typed output, honor "return" param --- */ // TODO: make this a deprecration passthrough exports._chart = chart; async function chart(symbol, queryOptionsOverrides, moduleOptions) { const returnAs = queryOptionsOverrides?.return || "array"; const result = (await this._moduleExec({ moduleName: "chart", query: { assertSymbol: symbol, url: "https://${YF_QUERY_HOST}/v8/finance/chart/" + symbol, definitions, schemaKey: "#/definitions/ChartOptions", defaults: queryOptionsDefaults, overrides: queryOptionsOverrides, transformWith(queryOptions) { 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.chart() option '" + fieldName + "' invalid date provided: '" + value + "'"); } queryOptions[fieldName] = Math.floor(timestamp / 1000); } } if (queryOptions.period1 === queryOptions.period2) { throw new Error("yahooFinance.chart() options `period1` and `period2` " + "cannot share the same value."); } // Don't pass this on to Yahoo delete queryOptions.return; return queryOptions; }, }, result: { definitions, schemaKey: "#/definitions/ChartResultObject", // deno-lint-ignore no-explicit-any transformWith(result) { if (!result.chart) { throw new Error("Unexpected result: " + JSON.stringify(result)); } const chart = result.chart.result[0]; // If there are no quotes, chart.timestamp will be empty, but Yahoo also // gives us chart.indicators.quotes = [{}]. Let's clean that up and // deliver an empty array rather than an invalid ChartIndicatorQuote/ if (!chart.timestamp) { if (chart.indicators.quote.length !== 1) { throw new Error("No timestamp with quotes.length !== 1, please report with your query"); } if (Object.keys(chart.indicators.quote[0]).length !== 0) { // i.e. {} throw new Error("No timestamp with unexpected quote, please report with your query" + JSON.stringify(chart.indicators.quote[0])); } chart.indicators.quote.pop(); } return chart; }, }, moduleOptions, })); if (returnAs === "object") { return result; } else if (returnAs === "array") { const timestamp = result.timestamp; /* seems as though yahoo inserts extra quotes at the event times, so no need. if (result.events) { for (let event of ["dividends", "splits"]) { // @ts-ignore if (result.events[event]) // @ts-ignore timestamp = timestamp.filter((ts) => !result.events[event][ts]); } } */ // istanbul ignore next if (timestamp && result?.indicators?.quote && result.indicators.quote[0].high.length !== timestamp.length) { console.log({ origTimestampSize: result.timestamp && result.timestamp.length, filteredSize: timestamp.length, quoteSize: result.indicators.quote[0].high.length, }); throw new Error("Timestamp count mismatch, please report this with the query you used"); } const result2 = { meta: result.meta, quotes: timestamp ? new Array(timestamp.length) : [], }; const adjclose = result?.indicators?.adjclose?.[0].adjclose; if (timestamp) { for (let i = 0; i < timestamp.length; i++) { result2.quotes[i] = { date: new Date(timestamp[i] * 1000), high: result.indicators.quote[0].high[i], volume: result.indicators.quote[0].volume[i], open: result.indicators.quote[0].open[i], low: result.indicators.quote[0].low[i], close: result.indicators.quote[0].close[i], }; if (adjclose) result2.quotes[i].adjclose = adjclose[i]; } } if (result.events) { result2.events = {}; for (const event of ["dividends", "splits"]) { if (result.events[event]) { result2.events[event] = Object.values(result.events[event]); } } } return result2; } // TypeScript runtime validation ensures no other values for // "returnAs" are possible. }