Browse Source

Bugfix/missing close price in historical market data (#7694)

* Fix missing close price in historical market data of Yahoo Finance service

* Discard market prices carried forward beyond the data of the data provider

* Update changelog
pull/7697/head
Thomas Kaul 6 days ago
committed by GitHub
parent
commit
a55b9f212b
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 2
      CHANGELOG.md
  2. 45
      apps/api/src/services/data-provider/yahoo-finance/yahoo-finance.service.ts
  3. 23
      apps/api/src/services/queues/data-gathering/data-gathering.processor.ts
  4. 4
      apps/api/src/services/queues/data-gathering/data-gathering.service.ts

2
CHANGELOG.md

@ -21,6 +21,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Fixed the benchmark label in the tooltip of the benchmark comparator on the analysis page - Fixed the benchmark label in the tooltip of the benchmark comparator on the analysis page
- Fixed the _Storybook_ setup by loading the `@angular/localize` polyfill centrally - Fixed the _Storybook_ setup by loading the `@angular/localize` polyfill centrally
- Fixed an issue in the activities import where an unused custom asset profile was created if the related activities were not imported - Fixed an issue in the activities import where an unused custom asset profile was created if the related activities were not imported
- Fixed the missing close price in the historical market data of the _Yahoo Finance_ service by falling back to the market price of the quote
- Fixed the historical market data gathering by no longer storing the last known market price for the most recent dates without data from the data provider
## 3.57.0 - 2026-08-21 ## 3.57.0 - 2026-08-21

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

@ -143,30 +143,39 @@ export class YahooFinanceService implements DataProviderInterface {
[date: string]: DataProviderHistoricalResponse; [date: string]: DataProviderHistoricalResponse;
}> { }> {
try { try {
const historicalResult = this.convertToHistoricalResult( const chartResult = await this.yahooFinance.chart(
await this.yahooFinance.chart( this.yahooFinanceDataEnhancerService.convertToYahooFinanceSymbol(
this.yahooFinanceDataEnhancerService.convertToYahooFinanceSymbol( symbol
symbol ),
), {
{ interval: '1d',
interval: '1d', period1: format(from, DATE_FORMAT),
period1: format(from, DATE_FORMAT), period2: format(
period2: format( isSameDay(from, to) ? addDays(to, 1) : to,
isSameDay(from, to) ? addDays(to, 1) : to, DATE_FORMAT
DATE_FORMAT )
) }
}
)
); );
const historicalResult = this.convertToHistoricalResult(chartResult);
const response: { const response: {
[date: string]: DataProviderHistoricalResponse; [date: string]: DataProviderHistoricalResponse;
} = {}; } = {};
for (const historicalItem of historicalResult) { for (const { close, date } of historicalResult) {
response[format(historicalItem.date, DATE_FORMAT)] = { // The chart endpoint can omit the close price of a historical item, for
marketPrice: historicalItem.close // example if the daily data of an exchange has not been consolidated yet.
}; // In this case, the market price of the corresponding quote is taken.
const marketPrice =
close ??
(isSameDay(date, chartResult.meta.regularMarketTime)
? chartResult.meta.regularMarketPrice
: undefined);
if (marketPrice) {
response[format(date, DATE_FORMAT)] = { marketPrice };
}
} }
return response; return response;

23
apps/api/src/services/queues/data-gathering/data-gathering.processor.ts

@ -125,6 +125,7 @@ export class DataGatheringProcessor {
const data: Prisma.MarketDataUpdateInput[] = []; const data: Prisma.MarketDataUpdateInput[] = [];
let lastMarketPrice: number; let lastMarketPrice: number;
let numberOfMarketDataItemsToKeep = 0;
while ( while (
isBefore( isBefore(
@ -139,15 +140,13 @@ export class DataGatheringProcessor {
) )
) )
) { ) {
if ( const marketPriceOfDataProvider =
historicalData[assetProfileIdentifier]?.[ historicalData[assetProfileIdentifier]?.[
format(currentDate, DATE_FORMAT) format(currentDate, DATE_FORMAT)
]?.marketPrice ]?.marketPrice;
) {
lastMarketPrice = if (marketPriceOfDataProvider) {
historicalData[assetProfileIdentifier]?.[ lastMarketPrice = marketPriceOfDataProvider;
format(currentDate, DATE_FORMAT)
]?.marketPrice;
} }
if (lastMarketPrice) { if (lastMarketPrice) {
@ -158,11 +157,21 @@ export class DataGatheringProcessor {
marketPrice: lastMarketPrice, marketPrice: lastMarketPrice,
state: 'CLOSE' state: 'CLOSE'
}); });
if (marketPriceOfDataProvider) {
numberOfMarketDataItemsToKeep = data.length;
}
} }
currentDate = addDays(currentDate, 1); currentDate = addDays(currentDate, 1);
} }
// A gap at the end means that the market data is not available yet, in
// contrast to a gap in between, which means that the market was closed.
// Therefore, the market prices which are carried forward after the last
// market price of the data provider are discarded.
data.splice(numberOfMarketDataItemsToKeep);
if (force) { if (force) {
await this.marketDataService.replaceForSymbol({ await this.marketDataService.replaceForSymbol({
data, data,

4
apps/api/src/services/queues/data-gathering/data-gathering.service.ts

@ -310,9 +310,9 @@ export class DataGatheringService {
}); });
const marketPrice = const marketPrice =
historicalData[getAssetProfileIdentifier({ dataSource, symbol })][ historicalData[getAssetProfileIdentifier({ dataSource, symbol })]?.[
format(date, DATE_FORMAT) format(date, DATE_FORMAT)
].marketPrice; ]?.marketPrice;
if (marketPrice) { if (marketPrice) {
return await this.prismaService.marketData.upsert({ return await this.prismaService.marketData.upsert({

Loading…
Cancel
Save