From b41eb60348788e16b255e2b0e61adac44036e6db Mon Sep 17 00:00:00 2001 From: helgehatt Date: Thu, 28 Mar 2024 18:23:56 +0100 Subject: [PATCH] Fix chart tooltip of benchmark comparator (#3167) * Fix chart tooltip of benchmark comparator * Update changelog --- CHANGELOG.md | 4 ++ .../src/app/benchmark/benchmark.service.ts | 46 +++++++++++-------- .../src/app/portfolio/current-rate.service.ts | 4 +- .../portfolio-calculator-no-orders.spec.ts | 12 ++--- .../src/app/portfolio/portfolio-calculator.ts | 27 ++++++----- .../src/app/portfolio/portfolio.service.ts | 14 ++---- .../benchmark-comparator.component.ts | 15 ++++-- 7 files changed, 72 insertions(+), 50 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4a2431c42..34b678ba9 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 - Extended the export functionality by the user account’s currency +### Fixed + +- Fixed the chart tooltip of the benchmark comparator + ## 2.67.0 - 2024-03-26 ### Added diff --git a/apps/api/src/app/benchmark/benchmark.service.ts b/apps/api/src/app/benchmark/benchmark.service.ts index e223eea50..6f2047210 100644 --- a/apps/api/src/app/benchmark/benchmark.service.ts +++ b/apps/api/src/app/benchmark/benchmark.service.ts @@ -13,7 +13,8 @@ import { import { DATE_FORMAT, calculateBenchmarkTrend, - parseDate + parseDate, + resetHours } from '@ghostfolio/common/helper'; import { Benchmark, @@ -27,7 +28,13 @@ import { BenchmarkTrend } from '@ghostfolio/common/types'; import { Injectable, Logger } from '@nestjs/common'; import { SymbolProfile } from '@prisma/client'; import { Big } from 'big.js'; -import { format, isSameDay, subDays } from 'date-fns'; +import { + differenceInDays, + eachDayOfInterval, + format, + isSameDay, + subDays +} from 'date-fns'; import { isNumber, last, uniqBy } from 'lodash'; import ms from 'ms'; @@ -208,15 +215,28 @@ export class BenchmarkService { public async getMarketDataBySymbol({ dataSource, + endDate = new Date(), startDate, symbol, userCurrency }: { + endDate?: Date; startDate: Date; userCurrency: string; } & UniqueAsset): Promise { const marketData: { date: string; value: number }[] = []; + const days = differenceInDays(endDate, startDate) + 1; + const dates = eachDayOfInterval( + { + start: startDate, + end: endDate + }, + { step: Math.round(days / Math.min(days, MAX_CHART_ITEMS)) } + ).map((date) => { + return resetHours(date); + }); + const [currentSymbolItem, marketDataItems] = await Promise.all([ this.symbolService.get({ dataGatheringItem: { @@ -232,7 +252,7 @@ export class BenchmarkService { dataSource, symbol, date: { - gte: startDate + in: dates } } }) @@ -266,17 +286,7 @@ export class BenchmarkService { return { marketData }; } - const step = Math.round( - marketDataItems.length / Math.min(marketDataItems.length, MAX_CHART_ITEMS) - ); - - let i = 0; - for (let marketDataItem of marketDataItems) { - if (i % step !== 0) { - continue; - } - const exchangeRate = exchangeRates[`${currentSymbolItem.currency}${userCurrency}`]?.[ format(marketDataItem.date, DATE_FORMAT) @@ -299,15 +309,15 @@ export class BenchmarkService { }); } - const includesToday = isSameDay( + const includesEndDate = isSameDay( parseDate(last(marketData).date), - new Date() + endDate ); - if (currentSymbolItem?.marketPrice && !includesToday) { + if (currentSymbolItem?.marketPrice && !includesEndDate) { const exchangeRate = exchangeRates[`${currentSymbolItem.currency}${userCurrency}`]?.[ - format(new Date(), DATE_FORMAT) + format(endDate, DATE_FORMAT) ]; const exchangeRateFactor = @@ -316,7 +326,7 @@ export class BenchmarkService { : 1; marketData.push({ - date: format(new Date(), DATE_FORMAT), + date: format(endDate, DATE_FORMAT), value: this.calculateChangeInPercentage( marketPriceAtStartDate, diff --git a/apps/api/src/app/portfolio/current-rate.service.ts b/apps/api/src/app/portfolio/current-rate.service.ts index f4855329e..712d07e7a 100644 --- a/apps/api/src/app/portfolio/current-rate.service.ts +++ b/apps/api/src/app/portfolio/current-rate.service.ts @@ -34,7 +34,7 @@ export class CurrentRateService { }: GetValuesParams): Promise { const dataProviderInfos: DataProviderInfo[] = []; - const includeToday = + const includesToday = (!dateQuery.lt || isBefore(new Date(), dateQuery.lt)) && (!dateQuery.gte || isBefore(dateQuery.gte, new Date())) && (!dateQuery.in || this.containsToday(dateQuery.in)); @@ -43,7 +43,7 @@ export class CurrentRateService { const quoteErrors: ResponseError['errors'] = []; const today = resetHours(new Date()); - if (includeToday) { + if (includesToday) { promises.push( this.dataProviderService .getQuotes({ items: dataGatheringItems, user: this.request?.user }) diff --git a/apps/api/src/app/portfolio/portfolio-calculator-no-orders.spec.ts b/apps/api/src/app/portfolio/portfolio-calculator-no-orders.spec.ts index 3d8c6b33c..fdbdb78bd 100644 --- a/apps/api/src/app/portfolio/portfolio-calculator-no-orders.spec.ts +++ b/apps/api/src/app/portfolio/portfolio-calculator-no-orders.spec.ts @@ -3,6 +3,7 @@ import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate- import { parseDate } from '@ghostfolio/common/helper'; import { Big } from 'big.js'; +import { subDays } from 'date-fns'; import { CurrentRateServiceMock } from './current-rate.service.mock'; import { PortfolioCalculator } from './portfolio-calculator'; @@ -46,13 +47,12 @@ describe('PortfolioCalculator', () => { .spyOn(Date, 'now') .mockImplementation(() => parseDate('2021-12-18').getTime()); - const chartData = await portfolioCalculator.getChartData({ - start: new Date() - }); + const start = subDays(new Date(Date.now()), 10); + + const chartData = await portfolioCalculator.getChartData({ start }); - const currentPositions = await portfolioCalculator.getCurrentPositions( - new Date() - ); + const currentPositions = + await portfolioCalculator.getCurrentPositions(start); const investments = portfolioCalculator.getInvestments(); diff --git a/apps/api/src/app/portfolio/portfolio-calculator.ts b/apps/api/src/app/portfolio/portfolio-calculator.ts index e7ded8f8c..438653fc6 100644 --- a/apps/api/src/app/portfolio/portfolio-calculator.ts +++ b/apps/api/src/app/portfolio/portfolio-calculator.ts @@ -18,6 +18,7 @@ import { addDays, addMilliseconds, differenceInDays, + eachDayOfInterval, endOfDay, format, isBefore, @@ -199,29 +200,31 @@ export class PortfolioCalculator { }) ?? []; const currencies: { [symbol: string]: string } = {}; - const dates: Date[] = []; const dataGatheringItems: IDataGatheringItem[] = []; const firstIndex = transactionPointsBeforeEndDate.length; - let day = start; + let dates = eachDayOfInterval({ start, end }, { step }).map((date) => { + return resetHours(date); + }); - while (isBefore(day, end)) { - dates.push(resetHours(day)); - day = addDays(day, step); - } + const includesEndDate = isSameDay(last(dates), end); - if (!isSameDay(last(dates), end)) { + if (!includesEndDate) { dates.push(resetHours(end)); } if (transactionPointsBeforeEndDate.length > 0) { - for (const item of transactionPointsBeforeEndDate[firstIndex - 1].items) { + for (const { + currency, + dataSource, + symbol + } of transactionPointsBeforeEndDate[firstIndex - 1].items) { dataGatheringItems.push({ - dataSource: item.dataSource, - symbol: item.symbol + dataSource, + symbol }); - currencies[item.symbol] = item.currency; - symbols[item.symbol] = true; + currencies[symbol] = currency; + symbols[symbol] = true; } } diff --git a/apps/api/src/app/portfolio/portfolio.service.ts b/apps/api/src/app/portfolio/portfolio.service.ts index 3517b8975..ce5fc998f 100644 --- a/apps/api/src/app/portfolio/portfolio.service.ts +++ b/apps/api/src/app/portfolio/portfolio.service.ts @@ -1482,17 +1482,13 @@ export class PortfolioService { userId = await this.getUserId(impersonationId, userId); - const endDate = new Date(); - const portfolioStart = parseDate(transactionPoints[0].date); const startDate = this.getStartDate(dateRange, portfolioStart); - - let step = 1; - - if (withDataDecimation) { - const daysInMarket = differenceInDays(new Date(), startDate); - step = Math.round(daysInMarket / Math.min(daysInMarket, MAX_CHART_ITEMS)); - } + const endDate = new Date(); + const daysInMarket = differenceInDays(endDate, startDate) + 1; + const step = withDataDecimation + ? Math.round(daysInMarket / Math.min(daysInMarket, MAX_CHART_ITEMS)) + : 1; const items = await portfolioCalculator.getChartData({ step, diff --git a/apps/client/src/app/components/benchmark-comparator/benchmark-comparator.component.ts b/apps/client/src/app/components/benchmark-comparator/benchmark-comparator.component.ts index fdf20023e..8b2d59754 100644 --- a/apps/client/src/app/components/benchmark-comparator/benchmark-comparator.component.ts +++ b/apps/client/src/app/components/benchmark-comparator/benchmark-comparator.component.ts @@ -98,6 +98,12 @@ export class BenchmarkComparatorComponent implements OnChanges, OnDestroy { } private initialize() { + const benchmarkDataValues: { [date: string]: number } = {}; + + for (const { date, value } of this.benchmarkDataItems) { + benchmarkDataValues[date] = value; + } + const data: ChartData<'line'> = { datasets: [ { @@ -113,8 +119,11 @@ export class BenchmarkComparatorComponent implements OnChanges, OnDestroy { backgroundColor: `rgb(${secondaryColorRgb.r}, ${secondaryColorRgb.g}, ${secondaryColorRgb.b})`, borderColor: `rgb(${secondaryColorRgb.r}, ${secondaryColorRgb.g}, ${secondaryColorRgb.b})`, borderWidth: 2, - data: this.benchmarkDataItems.map(({ date, value }) => { - return { x: parseDate(date).getTime(), y: value }; + data: this.performanceDataItems.map(({ date }) => { + return { + x: parseDate(date).getTime(), + y: benchmarkDataValues[date] + }; }), label: this.benchmark?.name ?? $localize`Benchmark` } @@ -228,7 +237,7 @@ export class BenchmarkComparatorComponent implements OnChanges, OnDestroy { locale: this.locale, unit: '%' }), - mode: 'x', + mode: 'index', position: 'top', xAlign: 'center', yAlign: 'bottom'