Browse Source

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
pull/7610/head
Thomas Kaul 5 days ago
committed by GitHub
parent
commit
f474f319bf
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 4
      CHANGELOG.md
  2. 8
      apps/api/src/services/data-provider/data-provider.service.ts
  3. 55
      apps/api/src/services/data-provider/yahoo-finance/yahoo-finance.service.ts

4
CHANGELOG.md

@ -11,6 +11,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Refreshed the cryptocurrencies list - 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 ## 3.49.0 - 2026-08-12
### Changed ### Changed

8
apps/api/src/services/data-provider/data-provider.service.ts

@ -340,7 +340,13 @@ export class DataProviderService implements OnModuleInit {
from: Date; from: Date;
granularity: Granularity; granularity: Granularity;
to: Date; to: Date;
} & AssetProfileIdentifier) { } & AssetProfileIdentifier): Promise<{
[date: string]: DataProviderHistoricalResponse;
}> {
if (!isValid(from) || !isValid(to)) {
return {};
}
return this.getDataProvider(DataSource[dataSource]).getDividends({ return this.getDataProvider(DataSource[dataSource]).getDividends({
from, from,
granularity, granularity,

55
apps/api/src/services/data-provider/yahoo-finance/yahoo-finance.service.ts

@ -22,6 +22,7 @@ import {
import { Injectable, Logger } from '@nestjs/common'; import { Injectable, Logger } from '@nestjs/common';
import { DataSource, SymbolProfile } from '@prisma/client'; import { DataSource, SymbolProfile } from '@prisma/client';
import { addDays, format, isSameDay } from 'date-fns'; import { addDays, format, isSameDay } from 'date-fns';
import { ReasonPhrases, StatusCodes } from 'http-status-codes';
import { uniqBy } from 'lodash'; import { uniqBy } from 'lodash';
import YahooFinance from 'yahoo-finance2'; import YahooFinance from 'yahoo-finance2';
import { ChartResultArray } from 'yahoo-finance2/esm/src/modules/chart'; import { ChartResultArray } from 'yahoo-finance2/esm/src/modules/chart';
@ -41,6 +42,12 @@ import { SearchQuoteNonYahoo } from 'yahoo-finance2/esm/src/modules/search';
@Injectable() @Injectable()
export class YahooFinanceService implements DataProviderInterface { 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 logger = new Logger(YahooFinanceService.name);
private readonly yahooFinance = new YahooFinance({ private readonly yahooFinance = new YahooFinance({
@ -77,10 +84,6 @@ export class YahooFinanceService implements DataProviderInterface {
symbol, symbol,
to to
}: GetDividendsParams) { }: GetDividendsParams) {
if (isSameDay(from, to)) {
to = addDays(to, 1);
}
try { try {
const historicalResult = this.convertToDividendResult( const historicalResult = this.convertToDividendResult(
await this.yahooFinance.chart( await this.yahooFinance.chart(
@ -91,7 +94,10 @@ export class YahooFinanceService implements DataProviderInterface {
events: 'dividends', events: 'dividends',
interval: granularity === 'month' ? '1mo' : '1d', interval: granularity === 'month' ? '1mo' : '1d',
period1: format(from, DATE_FORMAT), 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; return response;
} catch (error) { } catch (error) {
this.logger.error( const message = `Could not get dividends for ${symbol} (${this.getName()}) from ${format(
`Could not get dividends for ${symbol} (${this.getName()}) from ${format(
from, from,
DATE_FORMAT DATE_FORMAT
)} to ${format(to, DATE_FORMAT)}: [${error.name}] ${error.message}` )} 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 {}; return {};
} }
@ -125,10 +145,6 @@ export class YahooFinanceService implements DataProviderInterface {
}: GetHistoricalParams): Promise<{ }: GetHistoricalParams): Promise<{
[date: string]: DataProviderHistoricalResponse; [date: string]: DataProviderHistoricalResponse;
}> { }> {
if (isSameDay(from, to)) {
to = addDays(to, 1);
}
try { try {
const historicalResult = this.convertToHistoricalResult( const historicalResult = this.convertToHistoricalResult(
await this.yahooFinance.chart( await this.yahooFinance.chart(
@ -138,7 +154,10 @@ export class YahooFinanceService implements DataProviderInterface {
{ {
interval: '1d', interval: '1d',
period1: format(from, DATE_FORMAT), 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; return response;
} catch (error) { } catch (error) {
if (error.message === 'No data found, symbol may be delisted') { if (error?.message === YahooFinanceService.DELISTED_ERROR_MESSAGE) {
throw new AssetProfileDelistedError( throw new AssetProfileDelistedError(
`No data found, ${symbol} (${this.getName()}) may be delisted` `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( `Could not get historical market data for ${symbol} (${this.getName()}) from ${format(
from, from,
DATE_FORMAT 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( private convertToDividendResult(
result: ChartResultArray result: ChartResultArray
): HistoricalDividendsResult { ): HistoricalDividendsResult {
return result.events.dividends.map(({ amount: dividends, date }) => { return (result.events?.dividends ?? []).map(
({ amount: dividends, date }) => {
return { date, dividends }; return { date, dividends };
}); }
);
} }
private convertToHistoricalResult( private convertToHistoricalResult(

Loading…
Cancel
Save