From 5720a5f0eb8ffc607f146ee6b49044a4336f011d Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Tue, 25 Aug 2026 20:32:13 +0200 Subject: [PATCH] Fix missing benchmark in performance chart for calendar year date ranges --- .../benchmarks/benchmarks.service.spec.ts | 141 ------------------ .../benchmarks/benchmarks.service.ts | 9 +- 2 files changed, 5 insertions(+), 145 deletions(-) delete mode 100644 apps/api/src/app/endpoints/benchmarks/benchmarks.service.spec.ts diff --git a/apps/api/src/app/endpoints/benchmarks/benchmarks.service.spec.ts b/apps/api/src/app/endpoints/benchmarks/benchmarks.service.spec.ts deleted file mode 100644 index 91211d641..000000000 --- a/apps/api/src/app/endpoints/benchmarks/benchmarks.service.spec.ts +++ /dev/null @@ -1,141 +0,0 @@ -import { PortfolioService } from '@ghostfolio/api/app/portfolio/portfolio.service'; -import { SymbolService } from '@ghostfolio/api/app/symbol/symbol.service'; -import { BenchmarkService } from '@ghostfolio/api/services/benchmark/benchmark.service'; -import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; -import { MarketDataService } from '@ghostfolio/api/services/market-data/market-data.service'; - -import { BenchmarksService } from './benchmarks.service'; - -describe('BenchmarksService', () => { - let benchmarksService: BenchmarksService; - - const mockBenchmarkService = { - calculateChangeInPercentage: jest.fn( - (startPrice: number, endPrice: number) => { - return (endPrice - startPrice) / startPrice; - } - ) - }; - - const mockExchangeRateDataService = { - getExchangeRatesByCurrency: jest.fn().mockResolvedValue({}) - }; - - const mockMarketDataService = { - marketDataItems: jest.fn() - }; - - const mockPortfolioService = { - getPerformance: jest.fn() - }; - - const mockSymbolService = { - get: jest.fn() - }; - - beforeEach(() => { - jest.clearAllMocks(); - - benchmarksService = new BenchmarksService( - mockBenchmarkService as unknown as BenchmarkService, - mockExchangeRateDataService as unknown as ExchangeRateDataService, - mockMarketDataService as unknown as MarketDataService, - mockPortfolioService as unknown as PortfolioService, - mockSymbolService as unknown as SymbolService - ); - }); - - describe('getMarketDataForUser', () => { - it('returns benchmark data for a calendar year with the first chart date as baseline', async () => { - const startDate = new Date(2024, 11, 31, 23, 59, 59, 999); - const endDate = new Date(2025, 11, 31, 23, 59, 59, 999); - - mockPortfolioService.getPerformance.mockResolvedValue({ - chart: [ - { date: '2025-01-01', value: 0 }, - { date: '2025-06-30', value: 10 }, - { date: '2025-12-31', value: 20 } - ] - }); - - mockSymbolService.get.mockResolvedValue({ - currency: 'USD', - marketPrice: 150 - }); - - mockMarketDataService.marketDataItems.mockResolvedValue([ - { date: new Date(Date.UTC(2025, 0, 1)), marketPrice: 100 }, - { date: new Date(Date.UTC(2025, 5, 30)), marketPrice: 120 }, - { date: new Date(Date.UTC(2025, 11, 31)), marketPrice: 150 } - ]); - - const result = await benchmarksService.getMarketDataForUser({ - dataSource: 'YAHOO', - dateRange: '2025', - endDate, - filters: [], - startDate, - symbol: 'VOO', - userId: 'user-1', - userSettings: { baseCurrency: 'USD' } - }); - - expect(result.marketData.length).toBeGreaterThan(0); - - const baselineItem = result.marketData.find( - ({ date }) => date === '2025-01-01' - ); - expect(baselineItem).toBeDefined(); - expect(baselineItem.value).toBe(0); - - const junItem = result.marketData.find( - ({ date }) => date === '2025-06-30' - ); - expect(junItem).toEqual({ - date: '2025-06-30', - value: expect.closeTo(((120 - 100) / 100) * 100, 10) - }); - - const decItem = result.marketData.find( - ({ date }) => date === '2025-12-31' - ); - expect(decItem).toEqual({ - date: '2025-12-31', - value: expect.closeTo(((150 - 100) / 100) * 100, 10) - }); - }); - - it('uses resetHours(startDate) as the baseline when the chart is empty', async () => { - const startDate = new Date(2024, 11, 31, 23, 59, 59, 999); - - mockPortfolioService.getPerformance.mockResolvedValue({ - chart: [] - }); - - mockSymbolService.get.mockResolvedValue({ - currency: 'USD', - marketPrice: 150 - }); - - mockMarketDataService.marketDataItems.mockResolvedValue([ - { date: new Date(Date.UTC(2024, 11, 31)), marketPrice: 100 } - ]); - - const result = await benchmarksService.getMarketDataForUser({ - dataSource: 'YAHOO', - dateRange: '2025', - filters: [], - startDate, - symbol: 'VOO', - userId: 'user-1', - userSettings: { baseCurrency: 'USD' } - }); - - const baselineItem = result.marketData.find( - ({ date }) => date === '2024-12-31' - ); - expect(baselineItem).toBeDefined(); - expect(baselineItem.value).toBe(0); - }); - }); -}); diff --git a/apps/api/src/app/endpoints/benchmarks/benchmarks.service.ts b/apps/api/src/app/endpoints/benchmarks/benchmarks.service.ts index 2833dfe6f..beff7ffa6 100644 --- a/apps/api/src/app/endpoints/benchmarks/benchmarks.service.ts +++ b/apps/api/src/app/endpoints/benchmarks/benchmarks.service.ts @@ -88,10 +88,11 @@ export class BenchmarksService { return { marketData }; } - const baselineDate = - chart.length > 0 - ? resetHours(parseDate(chart[0].date)) - : resetHours(startDate); + if (chart.length === 0) { + return { marketData }; + } + + const baselineDate = resetHours(parseDate(chart[0].date)); const exchangeRates = await this.exchangeRateDataService.getExchangeRatesByCurrency({