From f474f319bfccc8b3e53669f247b673134524750c Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Thu, 13 Aug 2026 18:20:52 +0200 Subject: [PATCH] Bugfix/resolve error when fetching dividends from Yahoo Finance for date ranges without events (#7612) * Resolve error when fetching dividends for date ranges without events * Update changelog --- CHANGELOG.md | 4 ++ .../data-provider/data-provider.service.ts | 8 ++- .../yahoo-finance/yahoo-finance.service.ts | 63 ++++++++++++------- 3 files changed, 53 insertions(+), 22 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2dd4c0cb0..bb5353dcc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Refreshed the cryptocurrencies list +### Fixed + +- Resolved an error when fetching dividends from _Yahoo Finance_ for date ranges without events + ## 3.49.0 - 2026-08-12 ### Changed diff --git a/apps/api/src/services/data-provider/data-provider.service.ts b/apps/api/src/services/data-provider/data-provider.service.ts index c5de3e4e5..adb12f5f0 100644 --- a/apps/api/src/services/data-provider/data-provider.service.ts +++ b/apps/api/src/services/data-provider/data-provider.service.ts @@ -340,7 +340,13 @@ export class DataProviderService implements OnModuleInit { from: Date; granularity: Granularity; to: Date; - } & AssetProfileIdentifier) { + } & AssetProfileIdentifier): Promise<{ + [date: string]: DataProviderHistoricalResponse; + }> { + if (!isValid(from) || !isValid(to)) { + return {}; + } + return this.getDataProvider(DataSource[dataSource]).getDividends({ from, granularity, diff --git a/apps/api/src/services/data-provider/yahoo-finance/yahoo-finance.service.ts b/apps/api/src/services/data-provider/yahoo-finance/yahoo-finance.service.ts index 364354f6b..f59e91db4 100644 --- a/apps/api/src/services/data-provider/yahoo-finance/yahoo-finance.service.ts +++ b/apps/api/src/services/data-provider/yahoo-finance/yahoo-finance.service.ts @@ -22,6 +22,7 @@ import { import { Injectable, Logger } from '@nestjs/common'; import { DataSource, SymbolProfile } from '@prisma/client'; import { addDays, format, isSameDay } from 'date-fns'; +import { ReasonPhrases, StatusCodes } from 'http-status-codes'; import { uniqBy } from 'lodash'; import YahooFinance from 'yahoo-finance2'; import { ChartResultArray } from 'yahoo-finance2/esm/src/modules/chart'; @@ -41,6 +42,12 @@ import { SearchQuoteNonYahoo } from 'yahoo-finance2/esm/src/modules/search'; @Injectable() export class YahooFinanceService implements DataProviderInterface { + private static readonly DELISTED_ERROR_MESSAGE = + 'No data found, symbol may be delisted'; + + private static readonly RATE_LIMIT_ERROR_MESSAGE = + ReasonPhrases.TOO_MANY_REQUESTS; + private readonly logger = new Logger(YahooFinanceService.name); private readonly yahooFinance = new YahooFinance({ @@ -77,10 +84,6 @@ export class YahooFinanceService implements DataProviderInterface { symbol, to }: GetDividendsParams) { - if (isSameDay(from, to)) { - to = addDays(to, 1); - } - try { const historicalResult = this.convertToDividendResult( await this.yahooFinance.chart( @@ -91,7 +94,10 @@ export class YahooFinanceService implements DataProviderInterface { events: 'dividends', interval: granularity === 'month' ? '1mo' : '1d', period1: format(from, DATE_FORMAT), - period2: format(to, DATE_FORMAT) + period2: format( + isSameDay(from, to) ? addDays(to, 1) : to, + DATE_FORMAT + ) } ) ); @@ -107,12 +113,26 @@ export class YahooFinanceService implements DataProviderInterface { return response; } catch (error) { - this.logger.error( - `Could not get dividends for ${symbol} (${this.getName()}) from ${format( - from, - DATE_FORMAT - )} to ${format(to, DATE_FORMAT)}: [${error.name}] ${error.message}` - ); + const message = `Could not get dividends for ${symbol} (${this.getName()}) from ${format( + from, + DATE_FORMAT + )} to ${format(to, DATE_FORMAT)}`; + + if (error?.message === YahooFinanceService.DELISTED_ERROR_MESSAGE) { + this.logger.warn( + `${message}: ${YahooFinanceService.DELISTED_ERROR_MESSAGE}` + ); + } else if ( + (error?.name === 'HTTPError' && + error?.code === StatusCodes.TOO_MANY_REQUESTS) || + error?.message?.startsWith(YahooFinanceService.RATE_LIMIT_ERROR_MESSAGE) + ) { + this.logger.warn( + `${message}: ${YahooFinanceService.RATE_LIMIT_ERROR_MESSAGE}` + ); + } else { + this.logger.error(`${message}: [${error?.name}] ${error?.message}`); + } return {}; } @@ -125,10 +145,6 @@ export class YahooFinanceService implements DataProviderInterface { }: GetHistoricalParams): Promise<{ [date: string]: DataProviderHistoricalResponse; }> { - if (isSameDay(from, to)) { - to = addDays(to, 1); - } - try { const historicalResult = this.convertToHistoricalResult( await this.yahooFinance.chart( @@ -138,7 +154,10 @@ export class YahooFinanceService implements DataProviderInterface { { interval: '1d', period1: format(from, DATE_FORMAT), - period2: format(to, DATE_FORMAT) + period2: format( + isSameDay(from, to) ? addDays(to, 1) : to, + DATE_FORMAT + ) } ) ); @@ -155,7 +174,7 @@ export class YahooFinanceService implements DataProviderInterface { return response; } catch (error) { - if (error.message === 'No data found, symbol may be delisted') { + if (error?.message === YahooFinanceService.DELISTED_ERROR_MESSAGE) { throw new AssetProfileDelistedError( `No data found, ${symbol} (${this.getName()}) may be delisted` ); @@ -164,7 +183,7 @@ export class YahooFinanceService implements DataProviderInterface { `Could not get historical market data for ${symbol} (${this.getName()}) from ${format( from, DATE_FORMAT - )} to ${format(to, DATE_FORMAT)}: [${error.name}] ${error.message}` + )} to ${format(to, DATE_FORMAT)}: [${error?.name}] ${error?.message}` ); } } @@ -343,9 +362,11 @@ export class YahooFinanceService implements DataProviderInterface { private convertToDividendResult( result: ChartResultArray ): HistoricalDividendsResult { - return result.events.dividends.map(({ amount: dividends, date }) => { - return { date, dividends }; - }); + return (result.events?.dividends ?? []).map( + ({ amount: dividends, date }) => { + return { date, dividends }; + } + ); } private convertToHistoricalResult(