diff --git a/CHANGELOG.md b/CHANGELOG.md index 29a6a5205..6e2549a12 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Added support to change the appearance (dark mode) in user settings +- Added the total amount chart to the investment timeline - Setup the `prettier` plugin `prettier-plugin-organize-attributes` ### Changed @@ -971,8 +972,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed -- Display the value in base currency in the accounts table on mobile -- Display the value in base currency in the activities table on mobile +- Displayed the value in base currency in the accounts table on mobile +- Displayed the value in base currency in the activities table on mobile - Renamed `orders` to `activities` in import and export functionality - Harmonized the algebraic sign of `currentGrossPerformancePercent` and `currentNetPerformancePercent` with `currentGrossPerformance` and `currentNetPerformance` - Improved the pricing page diff --git a/apps/api/src/app/portfolio/portfolio-calculator.ts b/apps/api/src/app/portfolio/portfolio-calculator.ts index eee617708..7ca87994c 100644 --- a/apps/api/src/app/portfolio/portfolio-calculator.ts +++ b/apps/api/src/app/portfolio/portfolio-calculator.ts @@ -287,7 +287,10 @@ export class PortfolioCalculator { date, netPerformanceInPercentage, netPerformance: totalNetPerformanceValues[date].toNumber(), - value: netPerformanceInPercentage + totalInvestment: totalInvestmentValues[date].toNumber(), + value: totalInvestmentValues[date] + .plus(totalNetPerformanceValues[date]) + .toNumber() }; }); } diff --git a/apps/api/src/app/portfolio/portfolio.controller.ts b/apps/api/src/app/portfolio/portfolio.controller.ts index 680b4cd93..abff3a420 100644 --- a/apps/api/src/app/portfolio/portfolio.controller.ts +++ b/apps/api/src/app/portfolio/portfolio.controller.ts @@ -38,6 +38,7 @@ import { } from '@nestjs/common'; import { REQUEST } from '@nestjs/core'; import { AuthGuard } from '@nestjs/passport'; +import Big from 'big.js'; import { StatusCodes, getReasonPhrase } from 'http-status-codes'; import { PortfolioPositionDetail } from './interfaces/portfolio-position-detail.interface'; @@ -68,7 +69,7 @@ export class PortfolioController { @Headers('impersonation-id') impersonationId: string, @Query('accounts') filterByAccounts?: string, @Query('assetClasses') filterByAssetClasses?: string, - @Query('range') range?: DateRange, + @Query('range') dateRange: DateRange = 'max', @Query('tags') filterByTags?: string ): Promise { let hasError = false; @@ -88,9 +89,9 @@ export class PortfolioController { summary, totalValueInBaseCurrency } = await this.portfolioService.getDetails({ + dateRange, filters, impersonationId, - dateRange: range, userId: this.request.user.id }); @@ -183,6 +184,7 @@ export class PortfolioController { @UseGuards(AuthGuard('jwt')) public async getInvestments( @Headers('impersonation-id') impersonationId: string, + @Query('range') dateRange: DateRange = 'max', @Query('groupBy') groupBy?: GroupBy ): Promise { if ( @@ -198,12 +200,16 @@ export class PortfolioController { let investments: InvestmentItem[]; if (groupBy === 'month') { - investments = await this.portfolioService.getInvestments( + investments = await this.portfolioService.getInvestments({ + dateRange, impersonationId, - 'month' - ); + groupBy: 'month' + }); } else { - investments = await this.portfolioService.getInvestments(impersonationId); + investments = await this.portfolioService.getInvestments({ + dateRange, + impersonationId + }); } if ( @@ -221,7 +227,7 @@ export class PortfolioController { })); } - return { firstOrderDate: parseDate(investments[0]?.date), investments }; + return { investments }; } @Get('performance') @@ -230,7 +236,7 @@ export class PortfolioController { @Version('2') public async getPerformanceV2( @Headers('impersonation-id') impersonationId: string, - @Query('range') dateRange + @Query('range') dateRange: DateRange = 'max' ): Promise { const performanceInformation = await this.portfolioService.getPerformanceV2( { @@ -244,9 +250,29 @@ export class PortfolioController { this.request.user.Settings.settings.viewMode === 'ZEN' || this.userService.isRestrictedView(this.request.user) ) { + performanceInformation.chart = performanceInformation.chart.map( + ({ date, netPerformanceInPercentage, totalInvestment, value }) => { + return { + date, + netPerformanceInPercentage, + totalInvestment: new Big(totalInvestment) + .div(performanceInformation.performance.totalInvestment) + .toNumber(), + value: new Big(value) + .div(performanceInformation.performance.currentValue) + .toNumber() + }; + } + ); + performanceInformation.performance = nullifyValuesInObject( performanceInformation.performance, - ['currentGrossPerformance', 'currentNetPerformance', 'currentValue'] + [ + 'currentGrossPerformance', + 'currentNetPerformance', + 'currentValue', + 'totalInvestment' + ] ); } @@ -258,11 +284,11 @@ export class PortfolioController { @UseInterceptors(TransformDataSourceInResponseInterceptor) public async getPositions( @Headers('impersonation-id') impersonationId: string, - @Query('range') range + @Query('range') dateRange: DateRange = 'max' ): Promise { const result = await this.portfolioService.getPositions( impersonationId, - range + dateRange ); if ( diff --git a/apps/api/src/app/portfolio/portfolio.service.ts b/apps/api/src/app/portfolio/portfolio.service.ts index 636b8f39c..1a22f8f4b 100644 --- a/apps/api/src/app/portfolio/portfolio.service.ts +++ b/apps/api/src/app/portfolio/portfolio.service.ts @@ -207,11 +207,16 @@ export class PortfolioService { }; } - public async getInvestments( - aImpersonationId: string, - groupBy?: GroupBy - ): Promise { - const userId = await this.getUserId(aImpersonationId, this.request.user.id); + public async getInvestments({ + dateRange, + impersonationId, + groupBy + }: { + dateRange: DateRange; + impersonationId: string; + groupBy?: GroupBy; + }): Promise { + const userId = await this.getUserId(impersonationId, this.request.user.id); const { portfolioOrders, transactionPoints } = await this.getTransactionPoints({ @@ -283,98 +288,18 @@ export class PortfolioService { } } - return sortBy(investments, (investment) => { + investments = sortBy(investments, (investment) => { return investment.date; }); - } - public async getChart( - aImpersonationId: string, - aDateRange: DateRange = 'max' - ): Promise { - const userId = await this.getUserId(aImpersonationId, this.request.user.id); - - const { portfolioOrders, transactionPoints } = - await this.getTransactionPoints({ - userId - }); - - const portfolioCalculator = new PortfolioCalculator({ - currency: this.request.user.Settings.settings.baseCurrency, - currentRateService: this.currentRateService, - orders: portfolioOrders - }); - - portfolioCalculator.setTransactionPoints(transactionPoints); - if (transactionPoints.length === 0) { - return { - isAllTimeHigh: false, - isAllTimeLow: false, - items: [] - }; - } - let portfolioStart = parse( - transactionPoints[0].date, - DATE_FORMAT, - new Date() - ); - - // Get start date for the full portfolio because of because of the - // min and max calculation - portfolioStart = this.getStartDate('max', portfolioStart); - - const timelineSpecification: TimelineSpecification[] = [ - { - start: format(portfolioStart, DATE_FORMAT), - accuracy: 'day' - } - ]; - - const timelineInfo = await portfolioCalculator.calculateTimeline( - timelineSpecification, - format(new Date(), DATE_FORMAT) - ); - - const timeline = timelineInfo.timelinePeriods; - - const items = timeline - .filter((timelineItem) => timelineItem !== null) - .map((timelineItem) => ({ - date: timelineItem.date, - value: timelineItem.netPerformance.toNumber() - })); - - let lastItem = null; - if (timeline.length > 0) { - lastItem = timeline[timeline.length - 1]; - } - - let isAllTimeHigh = timelineInfo.maxNetPerformance?.eq( - lastItem?.netPerformance ?? 0 - ); - let isAllTimeLow = timelineInfo.minNetPerformance?.eq( - lastItem?.netPerformance ?? 0 - ); - if (isAllTimeHigh && isAllTimeLow) { - isAllTimeHigh = false; - isAllTimeLow = false; - } - - portfolioStart = startOfDay( - this.getStartDate( - aDateRange, - parse(transactionPoints[0].date, DATE_FORMAT, new Date()) - ) + const startDate = this.getStartDate( + dateRange, + parseDate(investments[0]?.date) ); - return { - isAllTimeHigh, - isAllTimeLow, - items: items.filter((item) => { - // Filter items of date range - return !isAfter(portfolioStart, parseDate(item.date)); - }) - }; + return investments.filter(({ date }) => { + return !isBefore(parseDate(date), startDate); + }); } public async getChartV2({ @@ -441,7 +366,7 @@ export class PortfolioService { filters?: Filter[]; withExcludedAccounts?: boolean; }): Promise { - // TODO: + // TODO userId = await this.getUserId(impersonationId, userId); const user = await this.userService.user({ id: userId }); @@ -979,13 +904,15 @@ export class PortfolioService { if (transactionPoints?.length <= 0) { return { chart: [], + firstOrderDate: undefined, hasErrors: false, performance: { currentGrossPerformance: 0, currentGrossPerformancePercent: 0, currentNetPerformance: 0, currentNetPerformancePercent: 0, - currentValue: 0 + currentValue: 0, + totalInvestment: 0 } }; } @@ -1006,6 +933,7 @@ export class PortfolioService { let currentNetPerformance = currentPositions.netPerformance; let currentNetPerformancePercent = currentPositions.netPerformancePercentage; + const totalInvestment = currentPositions.totalInvestment; // if (currentGrossPerformance.mul(currentGrossPerformancePercent).lt(0)) { // // If algebraic sign is different, harmonize it @@ -1035,14 +963,24 @@ export class PortfolioService { return { chart: historicalDataContainer.items.map( - ({ date, netPerformanceInPercentage }) => { + ({ + date, + netPerformance, + netPerformanceInPercentage, + totalInvestment, + value + }) => { return { date, - value: netPerformanceInPercentage + netPerformance, + netPerformanceInPercentage, + totalInvestment, + value }; } ), errors: currentPositions.errors, + firstOrderDate: parseDate(historicalDataContainer.items[0]?.date), hasErrors: currentPositions.hasErrors || hasErrors, performance: { currentValue, @@ -1050,7 +988,8 @@ export class PortfolioService { currentGrossPerformancePercent: currentGrossPerformancePercent.toNumber(), currentNetPerformance: currentNetPerformance.toNumber(), - currentNetPerformancePercent: currentNetPerformancePercent.toNumber() + currentNetPerformancePercent: currentNetPerformancePercent.toNumber(), + totalInvestment: totalInvestment.toNumber() } }; } diff --git a/apps/client/src/app/components/benchmark-comparator/benchmark-comparator.component.html b/apps/client/src/app/components/benchmark-comparator/benchmark-comparator.component.html index 1e5ca601c..95b7e272f 100644 --- a/apps/client/src/app/components/benchmark-comparator/benchmark-comparator.component.html +++ b/apps/client/src/app/components/benchmark-comparator/benchmark-comparator.component.html @@ -1,4 +1,4 @@ -
+
Performance @@ -31,14 +31,6 @@
-
- -
(); - @Output() dateRangeChanged = new EventEmitter(); @ViewChild('chartCanvas') chartCanvas; public chart: Chart; - public dateRangeOptions = ToggleComponent.DEFAULT_DATE_RANGE_OPTIONS; public constructor() { Chart.register( @@ -86,10 +82,6 @@ export class BenchmarkComparatorComponent implements OnChanges, OnDestroy { this.benchmarkChanged.next(symbolProfileId); } - public onChangeDateRange(dateRange: DateRange) { - this.dateRangeChanged.next(dateRange); - } - public ngOnDestroy() { this.chart?.destroy(); } diff --git a/apps/client/src/app/components/benchmark-comparator/benchmark-comparator.module.ts b/apps/client/src/app/components/benchmark-comparator/benchmark-comparator.module.ts index bc455ebc2..16440d3a3 100644 --- a/apps/client/src/app/components/benchmark-comparator/benchmark-comparator.module.ts +++ b/apps/client/src/app/components/benchmark-comparator/benchmark-comparator.module.ts @@ -2,7 +2,6 @@ import { CommonModule } from '@angular/common'; import { NgModule } from '@angular/core'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { MatSelectModule } from '@angular/material/select'; -import { GfToggleModule } from '@ghostfolio/client/components/toggle/toggle.module'; import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader'; import { BenchmarkComparatorComponent } from './benchmark-comparator.component'; @@ -13,7 +12,6 @@ import { BenchmarkComparatorComponent } from './benchmark-comparator.component'; imports: [ CommonModule, FormsModule, - GfToggleModule, MatSelectModule, NgxSkeletonLoaderModule, ReactiveFormsModule diff --git a/apps/client/src/app/components/home-market/home-market.component.ts b/apps/client/src/app/components/home-market/home-market.component.ts index 425ef0df7..2d2e1bffe 100644 --- a/apps/client/src/app/components/home-market/home-market.component.ts +++ b/apps/client/src/app/components/home-market/home-market.component.ts @@ -24,7 +24,7 @@ export class HomeMarketComponent implements OnDestroy, OnInit { public fearLabel = $localize`Fear`; public greedLabel = $localize`Greed`; public hasPermissionToAccessFearAndGreedIndex: boolean; - public historicalData: HistoricalDataItem[]; + public historicalDataItems: HistoricalDataItem[]; public info: InfoItem; public isLoading = true; public readonly numberOfDays = 180; @@ -67,7 +67,7 @@ export class HomeMarketComponent implements OnDestroy, OnInit { .pipe(takeUntil(this.unsubscribeSubject)) .subscribe(({ historicalData, marketPrice }) => { this.fearAndGreedIndex = marketPrice; - this.historicalData = [ + this.historicalDataItems = [ ...historicalData, { date: resetHours(new Date()).toISOString(), diff --git a/apps/client/src/app/components/home-market/home-market.html b/apps/client/src/app/components/home-market/home-market.html index b71c45e09..331d6f83c 100644 --- a/apps/client/src/app/components/home-market/home-market.html +++ b/apps/client/src/app/components/home-market/home-market.html @@ -10,7 +10,7 @@ symbol="Fear & Greed Index" yMax="100" yMin="0" - [historicalDataItems]="historicalData" + [historicalDataItems]="historicalDataItems" [isAnimated]="true" [locale]="user?.settings?.locale" [showXAxis]="true" diff --git a/apps/client/src/app/components/home-overview/home-overview.component.ts b/apps/client/src/app/components/home-overview/home-overview.component.ts index 72bbc4162..eb8f0c81c 100644 --- a/apps/client/src/app/components/home-overview/home-overview.component.ts +++ b/apps/client/src/app/components/home-overview/home-overview.component.ts @@ -116,12 +116,14 @@ export class HomeOverviewComponent implements OnDestroy, OnInit { this.performance = response.performance; this.isLoadingPerformance = false; - this.historicalDataItems = response.chart.map(({ date, value }) => { - return { - date, - value - }; - }); + this.historicalDataItems = response.chart.map( + ({ date, netPerformanceInPercentage }) => { + return { + date, + value: netPerformanceInPercentage + }; + } + ); this.changeDetectorRef.markForCheck(); }); 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 2c2b25172..cebc0f146 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 @@ -15,14 +15,16 @@ import { } from '@ghostfolio/common/chart-helper'; import { primaryColorRgb, secondaryColorRgb } from '@ghostfolio/common/config'; import { + DATE_FORMAT, getBackgroundColor, getDateFormatString, getTextColor, parseDate, transformTickToAbbreviation } from '@ghostfolio/common/helper'; +import { LineChartItem } from '@ghostfolio/common/interfaces'; import { InvestmentItem } from '@ghostfolio/common/interfaces/investment-item.interface'; -import { GroupBy } from '@ghostfolio/common/types'; +import { DateRange, GroupBy } from '@ghostfolio/common/types'; import { BarController, BarElement, @@ -35,7 +37,7 @@ import { Tooltip } from 'chart.js'; import annotationPlugin from 'chartjs-plugin-annotation'; -import { addDays, isAfter, parseISO, subDays } from 'date-fns'; +import { addDays, format, isAfter, parseISO, subDays } from 'date-fns'; @Component({ selector: 'gf-investment-chart', @@ -44,17 +46,19 @@ import { addDays, isAfter, parseISO, subDays } from 'date-fns'; styleUrls: ['./investment-chart.component.scss'] }) export class InvestmentChartComponent implements OnChanges, OnDestroy { + @Input() benchmarkDataItems: LineChartItem[] = []; @Input() currency: string; @Input() daysInMarket: number; @Input() groupBy: GroupBy; @Input() investments: InvestmentItem[]; @Input() isInPercent = false; @Input() locale: string; + @Input() range: DateRange = 'max'; @Input() savingsRate = 0; @ViewChild('chartCanvas') chartCanvas; - public chart: Chart; + public chart: Chart; public isLoading = true; private data: InvestmentItem[]; @@ -77,7 +81,7 @@ export class InvestmentChartComponent implements OnChanges, OnDestroy { } public ngOnChanges() { - if (this.investments) { + if (this.benchmarkDataItems && this.investments) { this.initialize(); } } @@ -93,41 +97,44 @@ export class InvestmentChartComponent implements OnChanges, OnDestroy { this.data = this.investments.map((a) => Object.assign({}, a)); if (!this.groupBy && this.data?.length > 0) { - // Extend chart by 5% of days in market (before) - const firstItem = this.data[0]; - this.data.unshift({ - ...firstItem, - date: subDays( - parseISO(firstItem.date), - this.daysInMarket * 0.05 || 90 - ).toISOString(), - investment: 0 - }); + if (this.range === 'max') { + // Extend chart by 5% of days in market (before) + const firstItem = this.data[0]; + this.data.unshift({ + ...firstItem, + date: format( + subDays(parseISO(firstItem.date), this.daysInMarket * 0.05 || 90), + DATE_FORMAT + ), + investment: 0 + }); + } // Extend chart by 5% of days in market (after) const lastItem = this.data[this.data.length - 1]; this.data.push({ ...lastItem, - date: addDays( - parseDate(lastItem.date), - this.daysInMarket * 0.05 || 90 - ).toISOString() + date: format( + addDays(parseDate(lastItem.date), this.daysInMarket * 0.05 || 90), + DATE_FORMAT + ) }); } const data = { - labels: this.data.map((investmentItem) => { - return investmentItem.date; + labels: this.benchmarkDataItems.map(({ date }) => { + return parseDate(date); }), datasets: [ { backgroundColor: `rgb(${primaryColorRgb.r}, ${primaryColorRgb.g}, ${primaryColorRgb.b})`, borderColor: `rgb(${primaryColorRgb.r}, ${primaryColorRgb.g}, ${primaryColorRgb.b})`, borderWidth: this.groupBy ? 0 : 2, - data: this.data.map((position) => { - return this.isInPercent - ? position.investment * 100 - : position.investment; + data: this.data.map(({ date, investment }) => { + return { + x: parseDate(date), + y: this.isInPercent ? investment * 100 : investment + }; }), label: $localize`Deposit`, segment: { @@ -139,6 +146,19 @@ export class InvestmentChartComponent implements OnChanges, OnDestroy { borderDash: (context: unknown) => this.isInFuture(context, [2, 2]) }, stepped: true + }, + { + borderColor: `rgb(${secondaryColorRgb.r}, ${secondaryColorRgb.g}, ${secondaryColorRgb.b})`, + borderWidth: 1, + data: this.benchmarkDataItems.map(({ date, value }) => { + return { + x: parseDate(date), + y: this.isInPercent ? value * 100 : value + }; + }), + fill: false, + label: $localize`Total Amount`, + pointRadius: 0 } ] }; diff --git a/apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts b/apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts index f9640521c..3b81390d9 100644 --- a/apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts +++ b/apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts @@ -1,4 +1,5 @@ import { ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core'; +import { ToggleComponent } from '@ghostfolio/client/components/toggle/toggle.component'; import { DataService } from '@ghostfolio/client/services/data.service'; import { ImpersonationStorageService } from '@ghostfolio/client/services/impersonation-storage.service'; import { UserService } from '@ghostfolio/client/services/user/user.service'; @@ -26,6 +27,7 @@ export class AnalysisPageComponent implements OnDestroy, OnInit { public benchmarkDataItems: HistoricalDataItem[] = []; public benchmarks: Partial[]; public bottom3: Position[]; + public dateRangeOptions = ToggleComponent.DEFAULT_DATE_RANGE_OPTIONS; public daysInMarket: number; public deviceType: string; public firstOrderDate: Date; @@ -33,12 +35,12 @@ export class AnalysisPageComponent implements OnDestroy, OnInit { public investments: InvestmentItem[]; public investmentsByMonth: InvestmentItem[]; public isLoadingBenchmarkComparator: boolean; - public mode: GroupBy; + public mode: GroupBy = 'month'; public modeOptions: ToggleOption[] = [ - { label: $localize`Monthly`, value: 'month' }, - { label: $localize`Accumulating`, value: undefined } + { label: $localize`Monthly`, value: 'month' } ]; public performanceDataItems: HistoricalDataItem[]; + public performanceDataItemsInPercentage: HistoricalDataItem[]; public top3: Position[]; public user: User; @@ -129,27 +131,41 @@ export class AnalysisPageComponent implements OnDestroy, OnInit { range: this.user?.settings?.dateRange }) .pipe(takeUntil(this.unsubscribeSubject)) - .subscribe(({ chart }) => { - this.firstOrderDate = new Date(chart?.[0]?.date ?? new Date()); - this.performanceDataItems = chart; - - this.updateBenchmarkDataItems(); + .subscribe(({ chart, firstOrderDate }) => { + this.firstOrderDate = firstOrderDate ?? new Date(); + this.daysInMarket = differenceInDays(new Date(), firstOrderDate); - this.changeDetectorRef.markForCheck(); - }); + this.investments = []; + this.performanceDataItems = []; + this.performanceDataItemsInPercentage = []; + + for (const { + date, + netPerformanceInPercentage, + totalInvestment, + value + } of chart) { + this.investments.push({ date, investment: totalInvestment }); + this.performanceDataItems.push({ + date, + value + }); + this.performanceDataItemsInPercentage.push({ + date, + value: netPerformanceInPercentage + }); + } - this.dataService - .fetchInvestments() - .pipe(takeUntil(this.unsubscribeSubject)) - .subscribe(({ firstOrderDate, investments }) => { - this.daysInMarket = differenceInDays(new Date(), firstOrderDate); - this.investments = investments; + this.updateBenchmarkDataItems(); this.changeDetectorRef.markForCheck(); }); this.dataService - .fetchInvestmentsByMonth() + .fetchInvestments({ + groupBy: 'month', + range: this.user?.settings?.dateRange + }) .pipe(takeUntil(this.unsubscribeSubject)) .subscribe(({ investments }) => { this.investmentsByMonth = investments; @@ -158,7 +174,7 @@ export class AnalysisPageComponent implements OnDestroy, OnInit { }); this.dataService - .fetchPositions({ range: 'max' }) + .fetchPositions({ range: this.user?.settings?.dateRange }) .pipe(takeUntil(this.unsubscribeSubject)) .subscribe(({ positions }) => { const positionsSorted = sortBy( 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 828fea343..f4624414f 100644 --- a/apps/client/src/app/pages/portfolio/analysis/analysis-page.html +++ b/apps/client/src/app/pages/portfolio/analysis/analysis-page.html @@ -1,5 +1,13 @@
-

Analysis

+

Analysis

+
+ +
@@ -96,6 +103,34 @@
+
+
+
+
+ Portfolio Evolution + +
+
+
+ +
+
+
+
@@ -117,24 +152,15 @@ >
-
diff --git a/apps/client/src/app/services/data.service.ts b/apps/client/src/app/services/data.service.ts index 58b58e67b..e36450425 100644 --- a/apps/client/src/app/services/data.service.ts +++ b/apps/client/src/app/services/data.service.ts @@ -163,34 +163,19 @@ export class DataService { return info; } - public fetchInvestments(): Observable { - return this.http.get('/api/v1/portfolio/investments').pipe( - map((response) => { - if (response.firstOrderDate) { - response.firstOrderDate = parseISO(response.firstOrderDate); - } - - return response; - }) + public fetchInvestments({ + groupBy, + range + }: { + groupBy?: 'month'; + range: DateRange; + }) { + return this.http.get( + '/api/v1/portfolio/investments', + { params: { groupBy, range } } ); } - public fetchInvestmentsByMonth(): Observable { - return this.http - .get('/api/v1/portfolio/investments', { - params: { groupBy: 'month' } - }) - .pipe( - map((response) => { - if (response.firstOrderDate) { - response.firstOrderDate = parseISO(response.firstOrderDate); - } - - return response; - }) - ); - } - public fetchSymbolItem({ dataSource, includeHistoricalData, @@ -252,11 +237,22 @@ export class DataService { ); } - public fetchPortfolioPerformance({ range }: { range: DateRange }) { - return this.http.get( - `/api/v2/portfolio/performance`, - { params: { range } } - ); + public fetchPortfolioPerformance({ + range + }: { + range: DateRange; + }): Observable { + return this.http + .get(`/api/v2/portfolio/performance`, { params: { range } }) + .pipe( + map((response) => { + if (response.firstOrderDate) { + response.firstOrderDate = parseISO(response.firstOrderDate); + } + + return response; + }) + ); } public fetchPortfolioPublic(aId: string) { diff --git a/apps/client/src/locales/messages.de.xlf b/apps/client/src/locales/messages.de.xlf index fe4b2d223..0de31b1cd 100644 --- a/apps/client/src/locales/messages.de.xlf +++ b/apps/client/src/locales/messages.de.xlf @@ -1722,7 +1722,7 @@ Zeitstrahl der Investitionen apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 105 + 140 @@ -1730,7 +1730,7 @@ Gewinner apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 26 + 33 @@ -1738,7 +1738,7 @@ Verlierer apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 62 + 69 @@ -2054,7 +2054,7 @@ Portfolio apps/client/src/app/components/benchmark-comparator/benchmark-comparator.component.ts - 107 + 99 apps/client/src/app/pages/public/public-page-routing.module.ts @@ -2498,15 +2498,7 @@ Monatlich apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 38 - - - - Accumulating - Aufsummiert - - apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 39 + 40 @@ -2514,11 +2506,11 @@ Einlage apps/client/src/app/components/investment-chart/investment-chart.component.ts - 132 + 139 libs/ui/src/lib/fire-calculator/fire-calculator.component.ts - 276 + 279 @@ -2526,7 +2518,7 @@ Verzinsung libs/ui/src/lib/fire-calculator/fire-calculator.component.ts - 286 + 289 @@ -2534,7 +2526,7 @@ Ersparnisse libs/ui/src/lib/fire-calculator/fire-calculator.component.ts - 296 + 299 @@ -2646,7 +2638,7 @@ Benchmark apps/client/src/app/components/benchmark-comparator/benchmark-comparator.component.ts - 116 + 108 @@ -2721,6 +2713,22 @@ 212 + + Total Amount + Gesamtbetrag + + apps/client/src/app/components/investment-chart/investment-chart.component.ts + 160 + + + + Portfolio Evolution + Portfolio Wertentwicklung + + apps/client/src/app/pages/portfolio/analysis/analysis-page.html + 112 + + diff --git a/apps/client/src/locales/messages.es.xlf b/apps/client/src/locales/messages.es.xlf index 363fb7c2a..493631105 100644 --- a/apps/client/src/locales/messages.es.xlf +++ b/apps/client/src/locales/messages.es.xlf @@ -1723,7 +1723,7 @@ Cronología de la inversión apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 105 + 140 @@ -1731,7 +1731,7 @@ Lo mejor apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 26 + 33 @@ -1739,7 +1739,7 @@ Lo peor apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 62 + 69 @@ -2055,7 +2055,7 @@ Cartera apps/client/src/app/components/benchmark-comparator/benchmark-comparator.component.ts - 107 + 99 apps/client/src/app/pages/public/public-page-routing.module.ts @@ -2483,15 +2483,7 @@ Ahorros libs/ui/src/lib/fire-calculator/fire-calculator.component.ts - 296 - - - - Accumulating - Acumulando - - apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 39 + 299 @@ -2507,7 +2499,7 @@ Interés libs/ui/src/lib/fire-calculator/fire-calculator.component.ts - 286 + 289 @@ -2515,11 +2507,11 @@ Depósito apps/client/src/app/components/investment-chart/investment-chart.component.ts - 132 + 139 libs/ui/src/lib/fire-calculator/fire-calculator.component.ts - 276 + 279 @@ -2535,7 +2527,7 @@ Mensual apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 38 + 40 @@ -2631,7 +2623,7 @@ Benchmark apps/client/src/app/components/benchmark-comparator/benchmark-comparator.component.ts - 116 + 108 @@ -2722,6 +2714,22 @@ 212 + + Total Amount + Total Amount + + apps/client/src/app/components/investment-chart/investment-chart.component.ts + 160 + + + + Portfolio Evolution + Portfolio Evolution + + apps/client/src/app/pages/portfolio/analysis/analysis-page.html + 112 + + diff --git a/apps/client/src/locales/messages.it.xlf b/apps/client/src/locales/messages.it.xlf index acde0f426..3170073ac 100644 --- a/apps/client/src/locales/messages.it.xlf +++ b/apps/client/src/locales/messages.it.xlf @@ -1723,7 +1723,7 @@ Cronologia degli investimenti apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 105 + 140 @@ -1731,7 +1731,7 @@ In alto apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 26 + 33 @@ -1739,7 +1739,7 @@ In basso apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 62 + 69 @@ -2055,7 +2055,7 @@ Portafoglio apps/client/src/app/components/benchmark-comparator/benchmark-comparator.component.ts - 107 + 99 apps/client/src/app/pages/public/public-page-routing.module.ts @@ -2483,15 +2483,7 @@ Risparmio libs/ui/src/lib/fire-calculator/fire-calculator.component.ts - 296 - - - - Accumulating - Accumulo - - apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 39 + 299 @@ -2507,7 +2499,7 @@ Interesse libs/ui/src/lib/fire-calculator/fire-calculator.component.ts - 286 + 289 @@ -2515,11 +2507,11 @@ Deposito apps/client/src/app/components/investment-chart/investment-chart.component.ts - 132 + 139 libs/ui/src/lib/fire-calculator/fire-calculator.component.ts - 276 + 279 @@ -2535,7 +2527,7 @@ Mensile apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 38 + 40 @@ -2631,7 +2623,7 @@ Benchmark apps/client/src/app/components/benchmark-comparator/benchmark-comparator.component.ts - 116 + 108 @@ -2722,6 +2714,22 @@ 212 + + Total Amount + Total Amount + + apps/client/src/app/components/investment-chart/investment-chart.component.ts + 160 + + + + Portfolio Evolution + Portfolio Evolution + + apps/client/src/app/pages/portfolio/analysis/analysis-page.html + 112 + + diff --git a/apps/client/src/locales/messages.nl.xlf b/apps/client/src/locales/messages.nl.xlf index 70bac87e0..bb45a5fd9 100644 --- a/apps/client/src/locales/messages.nl.xlf +++ b/apps/client/src/locales/messages.nl.xlf @@ -1722,7 +1722,7 @@ Tijdlijn investeringen apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 105 + 140 @@ -1730,7 +1730,7 @@ Top apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 26 + 33 @@ -1738,7 +1738,7 @@ Onder apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 62 + 69 @@ -2054,7 +2054,7 @@ Portefeuille apps/client/src/app/components/benchmark-comparator/benchmark-comparator.component.ts - 107 + 99 apps/client/src/app/pages/public/public-page-routing.module.ts @@ -2482,15 +2482,7 @@ Besparingen libs/ui/src/lib/fire-calculator/fire-calculator.component.ts - 296 - - - - Accumulating - Accumuleren - - apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 39 + 299 @@ -2506,7 +2498,7 @@ Rente libs/ui/src/lib/fire-calculator/fire-calculator.component.ts - 286 + 289 @@ -2514,11 +2506,11 @@ Storting apps/client/src/app/components/investment-chart/investment-chart.component.ts - 132 + 139 libs/ui/src/lib/fire-calculator/fire-calculator.component.ts - 276 + 279 @@ -2534,7 +2526,7 @@ Maandelijks apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 38 + 40 @@ -2630,7 +2622,7 @@ Benchmark apps/client/src/app/components/benchmark-comparator/benchmark-comparator.component.ts - 116 + 108 @@ -2721,6 +2713,22 @@ 212 + + Total Amount + Total Amount + + apps/client/src/app/components/investment-chart/investment-chart.component.ts + 160 + + + + Portfolio Evolution + Portfolio Evolution + + apps/client/src/app/pages/portfolio/analysis/analysis-page.html + 112 + + diff --git a/apps/client/src/locales/messages.xlf b/apps/client/src/locales/messages.xlf index e8620fc8d..a9003dc11 100644 --- a/apps/client/src/locales/messages.xlf +++ b/apps/client/src/locales/messages.xlf @@ -1547,21 +1547,21 @@ Investment Timeline apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 105 + 140 Top apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 26 + 33 Bottom apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 62 + 69 @@ -1842,7 +1842,7 @@ Portfolio apps/client/src/app/components/benchmark-comparator/benchmark-comparator.component.ts - 107 + 99 apps/client/src/app/pages/public/public-page-routing.module.ts @@ -2220,14 +2220,7 @@ Savings libs/ui/src/lib/fire-calculator/fire-calculator.component.ts - 296 - - - - Accumulating - - apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 39 + 299 @@ -2241,18 +2234,18 @@ Interest libs/ui/src/lib/fire-calculator/fire-calculator.component.ts - 286 + 289 Deposit apps/client/src/app/components/investment-chart/investment-chart.component.ts - 132 + 139 libs/ui/src/lib/fire-calculator/fire-calculator.component.ts - 276 + 279 @@ -2266,7 +2259,7 @@ Monthly apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 38 + 40 @@ -2351,7 +2344,7 @@ Benchmark apps/client/src/app/components/benchmark-comparator/benchmark-comparator.component.ts - 116 + 108 @@ -2431,6 +2424,20 @@ 195 + + Total Amount + + apps/client/src/app/components/investment-chart/investment-chart.component.ts + 160 + + + + Portfolio Evolution + + apps/client/src/app/pages/portfolio/analysis/analysis-page.html + 112 + + diff --git a/libs/common/src/lib/interfaces/historical-data-item.interface.ts b/libs/common/src/lib/interfaces/historical-data-item.interface.ts index 324957838..dd7bb84d8 100644 --- a/libs/common/src/lib/interfaces/historical-data-item.interface.ts +++ b/libs/common/src/lib/interfaces/historical-data-item.interface.ts @@ -4,5 +4,6 @@ export interface HistoricalDataItem { grossPerformancePercent?: number; netPerformance?: number; netPerformanceInPercentage?: number; - value: number; + totalInvestment?: number; + value?: number; } diff --git a/libs/common/src/lib/interfaces/portfolio-investments.interface.ts b/libs/common/src/lib/interfaces/portfolio-investments.interface.ts index 06e91fbd2..84ac0895f 100644 --- a/libs/common/src/lib/interfaces/portfolio-investments.interface.ts +++ b/libs/common/src/lib/interfaces/portfolio-investments.interface.ts @@ -1,6 +1,5 @@ import { InvestmentItem } from './investment-item.interface'; export interface PortfolioInvestments { - firstOrderDate: Date; investments: InvestmentItem[]; } diff --git a/libs/common/src/lib/interfaces/portfolio-performance.interface.ts b/libs/common/src/lib/interfaces/portfolio-performance.interface.ts index 670d69018..5a4f91023 100644 --- a/libs/common/src/lib/interfaces/portfolio-performance.interface.ts +++ b/libs/common/src/lib/interfaces/portfolio-performance.interface.ts @@ -5,4 +5,5 @@ export interface PortfolioPerformance { currentNetPerformance: number; currentNetPerformancePercent: number; currentValue: number; + totalInvestment: number; } diff --git a/libs/common/src/lib/interfaces/responses/portfolio-performance-response.interface.ts b/libs/common/src/lib/interfaces/responses/portfolio-performance-response.interface.ts index 74e7801cd..b0c453514 100644 --- a/libs/common/src/lib/interfaces/responses/portfolio-performance-response.interface.ts +++ b/libs/common/src/lib/interfaces/responses/portfolio-performance-response.interface.ts @@ -4,5 +4,6 @@ import { ResponseError } from './errors.interface'; export interface PortfolioPerformanceResponse extends ResponseError { chart?: HistoricalDataItem[]; + firstOrderDate: Date; performance: PortfolioPerformance; }