From 9d45ea7c0ea54c8ccccb3917742c2acf594fa298 Mon Sep 17 00:00:00 2001 From: Finn Fischer Date: Tue, 24 Jun 2025 11:48:07 +0600 Subject: [PATCH] improve chart scaling and date range handling --- .../investment-chart.component.ts | 81 ++++++++++++++----- .../portfolio/analysis/analysis-page.html | 3 + libs/common/src/lib/helper.ts | 9 +++ 3 files changed, 74 insertions(+), 19 deletions(-) diff --git a/apps/client/src/app/components/investment-chart/investment-chart.component.ts b/apps/client/src/app/components/investment-chart/investment-chart.component.ts index 9fd245485..e948a8ad4 100644 --- a/apps/client/src/app/components/investment-chart/investment-chart.component.ts +++ b/apps/client/src/app/components/investment-chart/investment-chart.component.ts @@ -7,14 +7,13 @@ import { import { primaryColorRgb, secondaryColorRgb } from '@ghostfolio/common/config'; import { getBackgroundColor, - getDateFormatString, getLocale, getTextColor, parseDate } from '@ghostfolio/common/helper'; import { LineChartItem } from '@ghostfolio/common/interfaces'; import { InvestmentItem } from '@ghostfolio/common/interfaces/investment-item.interface'; -import { ColorScheme, GroupBy } from '@ghostfolio/common/types'; +import { ColorScheme, DateRange, GroupBy } from '@ghostfolio/common/types'; import { ChangeDetectionStrategy, @@ -39,7 +38,14 @@ import { } from 'chart.js'; import 'chartjs-adapter-date-fns'; import annotationPlugin from 'chartjs-plugin-annotation'; -import { isAfter } from 'date-fns'; +import { + isAfter, + subDays, + subYears, + startOfMonth, + startOfYear, + startOfWeek +} from 'date-fns'; @Component({ selector: 'gf-investment-chart', @@ -50,9 +56,10 @@ import { isAfter } from 'date-fns'; }) export class InvestmentChartComponent implements OnChanges, OnDestroy { @Input() benchmarkDataItems: InvestmentItem[] = []; - @Input() benchmarkDataLabel = ''; + @Input() benchmarkDataLabel = $localize`Benchmark`; @Input() colorScheme: ColorScheme; @Input() currency: string; + @Input() dateRange: DateRange; @Input() groupBy: GroupBy; @Input() historicalDataItems: LineChartItem[] = []; @Input() isInPercent = false; @@ -158,6 +165,8 @@ export class InvestmentChartComponent implements OnChanges, OnDestroy { this.chart.options.plugins.tooltip = this.getTooltipPluginConfiguration() as unknown; + this.chart.options.scales.x = this.getChartXAxis() as any; + if ( this.savingsRate && // @ts-ignore @@ -230,21 +239,7 @@ export class InvestmentChartComponent implements OnChanges, OnDestroy { } as unknown, responsive: true, scales: { - x: { - border: { - color: `rgba(${getTextColor(this.colorScheme)}, 0.1)`, - width: this.groupBy ? 0 : 1 - }, - display: true, - grid: { - display: false - }, - type: 'time', - time: { - tooltipFormat: getDateFormatString(this.locale), - unit: 'year' - } - }, + x: this.getChartXAxis() as any, y: { border: { display: false @@ -284,6 +279,54 @@ export class InvestmentChartComponent implements OnChanges, OnDestroy { } } + private getChartXAxis() { + let min: number; + let unit: 'day' | 'month' | 'week' | 'year' = 'year'; + + switch (this.dateRange) { + case '1d': + min = subDays(new Date(), 1).getTime(); + unit = 'day'; + break; + case 'mtd': + min = startOfMonth(new Date()).getTime(); + unit = 'day'; + break; + case 'wtd': + min = startOfWeek(new Date()).getTime(); + unit = 'day'; + break; + case 'ytd': + min = startOfYear(new Date()).getTime(); + unit = 'month'; + break; + case '1y': + min = subYears(new Date(), 1).getTime(); + unit = 'month'; + break; + case '5y': + min = subYears(new Date(), 5).getTime(); + unit = 'year'; + break; + } + + return { + border: { + color: `rgba(${getTextColor(this.colorScheme)}, 0.1)`, + width: this.groupBy ? 0 : 1 + }, + display: true, + grid: { + display: false + }, + min, + type: 'time', + time: { + unit + } + }; + } + private getTooltipPluginConfiguration() { return { ...getTooltipOptions({ diff --git a/apps/client/src/app/pages/portfolio/analysis/analysis-page.html b/apps/client/src/app/pages/portfolio/analysis/analysis-page.html index 56c95e40f..a69a3ec22 100644 --- a/apps/client/src/app/pages/portfolio/analysis/analysis-page.html +++ b/apps/client/src/app/pages/portfolio/analysis/analysis-page.html @@ -350,6 +350,7 @@ [benchmarkDataItems]="investments" [benchmarkDataLabel]="portfolioEvolutionDataLabel" [currency]="user?.settings?.baseCurrency" + [dateRange]="user?.settings?.dateRange" [historicalDataItems]="performanceDataItems" [isInPercent]="hasImpersonationId || user.settings.isRestrictedView" [isLoading]="isLoadingInvestmentChart" @@ -406,6 +407,7 @@ [benchmarkDataItems]="investmentsByGroup" [benchmarkDataLabel]="investmentTimelineDataLabel" [currency]="user?.settings?.baseCurrency" + [dateRange]="user?.settings?.dateRange" [groupBy]="mode" [isInPercent]="hasImpersonationId || user.settings.isRestrictedView" [isLoading]="isLoadingInvestmentTimelineChart" @@ -441,6 +443,7 @@ [benchmarkDataItems]="dividendsByGroup" [benchmarkDataLabel]="dividendTimelineDataLabel" [currency]="user?.settings?.baseCurrency" + [dateRange]="user?.settings?.dateRange" [groupBy]="mode" [isInPercent]="hasImpersonationId || user.settings.isRestrictedView" [isLoading]="isLoadingDividendTimelineChart" diff --git a/libs/common/src/lib/helper.ts b/libs/common/src/lib/helper.ts index e5dc187ff..9eb2945f5 100644 --- a/libs/common/src/lib/helper.ts +++ b/libs/common/src/lib/helper.ts @@ -444,3 +444,12 @@ export function resolveMarketCondition( return { emoji: undefined }; } } + +export function isBenchmark( + aSymbol: string, + aBenchmarks: AssetProfileIdentifier[] +) { + return aBenchmarks.some(({ symbol }) => { + return symbol === aSymbol; + }); +}