diff --git a/apps/api/src/app/portfolio/portfolio.controller.ts b/apps/api/src/app/portfolio/portfolio.controller.ts index fc443ab93..86bc8c2fa 100644 --- a/apps/api/src/app/portfolio/portfolio.controller.ts +++ b/apps/api/src/app/portfolio/portfolio.controller.ts @@ -168,13 +168,19 @@ export class PortfolioController { }) ]; - const { accounts, holdings, hasErrors } = - await this.portfolioService.getDetails( - impersonationId, - this.request.user.id, - range, - filters - ); + const { + accounts, + filteredValueInBaseCurrency, + filteredValueInPercentage, + hasErrors, + holdings, + totalValueInBaseCurrency + } = await this.portfolioService.getDetails( + impersonationId, + this.request.user.id, + range, + filters + ); if (hasErrors || hasNotDefinedValuesInObject(holdings)) { hasError = true; @@ -234,8 +240,11 @@ export class PortfolioController { return { accounts, + filteredValueInBaseCurrency, + filteredValueInPercentage, hasError, - holdings + holdings, + totalValueInBaseCurrency }; } diff --git a/apps/api/src/app/portfolio/portfolio.service.ts b/apps/api/src/app/portfolio/portfolio.service.ts index d8aed4c17..397a52256 100644 --- a/apps/api/src/app/portfolio/portfolio.service.ts +++ b/apps/api/src/app/portfolio/portfolio.service.ts @@ -474,12 +474,21 @@ export class PortfolioService { }); const holdings: PortfolioDetails['holdings'] = {}; - const totalInvestment = currentPositions.totalInvestment.plus( - cashDetails.balanceInBaseCurrency - ); - const totalValue = currentPositions.currentValue.plus( + const totalInvestmentInBaseCurrency = currentPositions.totalInvestment.plus( cashDetails.balanceInBaseCurrency ); + let filteredValueInBaseCurrency = currentPositions.currentValue; + + if ( + aFilters?.length === 0 || + (aFilters?.length === 1 && + aFilters[0].type === 'ASSET_CLASS' && + aFilters[0].id === 'CASH') + ) { + filteredValueInBaseCurrency = filteredValueInBaseCurrency.plus( + cashDetails.balanceInBaseCurrency + ); + } const dataGatheringItems = currentPositions.positions.map((position) => { return { @@ -540,10 +549,12 @@ export class PortfolioService { holdings[item.symbol] = { markets, - allocationCurrent: totalValue.eq(0) + allocationCurrent: filteredValueInBaseCurrency.eq(0) ? 0 - : value.div(totalValue).toNumber(), - allocationInvestment: item.investment.div(totalInvestment).toNumber(), + : value.div(filteredValueInBaseCurrency).toNumber(), + allocationInvestment: item.investment + .div(totalInvestmentInBaseCurrency) + .toNumber(), assetClass: symbolProfile.assetClass, assetSubClass: symbolProfile.assetSubClass, countries: symbolProfile.countries, @@ -577,8 +588,8 @@ export class PortfolioService { cashDetails, emergencyFund, userCurrency, - investment: totalInvestment, - value: totalValue + investment: totalInvestmentInBaseCurrency, + value: filteredValueInBaseCurrency }); for (const symbol of Object.keys(cashPositions)) { @@ -594,7 +605,18 @@ export class PortfolioService { filters: aFilters }); - return { accounts, holdings, hasErrors: currentPositions.hasErrors }; + const summary = await this.getSummary(userId); + + return { + accounts, + holdings, + filteredValueInBaseCurrency: filteredValueInBaseCurrency.toNumber(), + filteredValueInPercentage: summary.netWorth + ? filteredValueInBaseCurrency.div(summary.netWorth).toNumber() + : 0, + hasErrors: currentPositions.hasErrors, + totalValueInBaseCurrency: summary.netWorth + }; } public async getPosition( @@ -920,11 +942,9 @@ export class PortfolioService { } public async getPerformance( - aImpersonationId: string, + userId: string, aDateRange: DateRange = 'max' ): Promise { - const userId = await this.getUserId(aImpersonationId, this.request.user.id); - const { portfolioOrders, transactionPoints } = await this.getTransactionPoints({ userId @@ -1082,12 +1102,11 @@ export class PortfolioService { }; } - public async getSummary(aImpersonationId: string): Promise { - const userCurrency = this.request.user.Settings.settings.baseCurrency; - const userId = await this.getUserId(aImpersonationId, this.request.user.id); + public async getSummary(userId: string): Promise { const user = await this.userService.user({ id: userId }); + const userCurrency = this.request.user.Settings.settings.baseCurrency; - const performanceInformation = await this.getPerformance(aImpersonationId); + const performanceInformation = await this.getPerformance(userId); const { balanceInBaseCurrency } = await this.accountService.getCashDetails({ userId, diff --git a/apps/api/src/interceptors/redact-values-in-response.interceptor.ts b/apps/api/src/interceptors/redact-values-in-response.interceptor.ts index b5889328d..4a9c5bef2 100644 --- a/apps/api/src/interceptors/redact-values-in-response.interceptor.ts +++ b/apps/api/src/interceptors/redact-values-in-response.interceptor.ts @@ -41,6 +41,14 @@ export class RedactValuesInResponseInterceptor return activity; }); } + + if (data.filteredValueInBaseCurrency) { + data.filteredValueInBaseCurrency = null; + } + + if (data.totalValueInBaseCurrency) { + data.totalValueInBaseCurrency = null; + } } return data; diff --git a/apps/client/src/app/pages/portfolio/allocations/allocations-page.html b/apps/client/src/app/pages/portfolio/allocations/allocations-page.html index 51aa49ae2..d6033ae6e 100644 --- a/apps/client/src/app/pages/portfolio/allocations/allocations-page.html +++ b/apps/client/src/app/pages/portfolio/allocations/allocations-page.html @@ -10,6 +10,17 @@ > +
+
+
+ +
+
+
diff --git a/apps/client/src/app/pages/portfolio/allocations/allocations-page.module.ts b/apps/client/src/app/pages/portfolio/allocations/allocations-page.module.ts index 65594048b..172db1b25 100644 --- a/apps/client/src/app/pages/portfolio/allocations/allocations-page.module.ts +++ b/apps/client/src/app/pages/portfolio/allocations/allocations-page.module.ts @@ -1,6 +1,7 @@ import { CommonModule } from '@angular/common'; import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core'; import { MatCardModule } from '@angular/material/card'; +import { MatProgressBarModule } from '@angular/material/progress-bar'; import { GfToggleModule } from '@ghostfolio/client/components/toggle/toggle.module'; import { GfWorldMapChartModule } from '@ghostfolio/client/components/world-map-chart/world-map-chart.module'; import { GfActivitiesFilterModule } from '@ghostfolio/ui/activities-filter/activities-filter.module'; @@ -22,7 +23,8 @@ import { AllocationsPageComponent } from './allocations-page.component'; GfToggleModule, GfWorldMapChartModule, GfValueModule, - MatCardModule + MatCardModule, + MatProgressBarModule ], schemas: [CUSTOM_ELEMENTS_SCHEMA] }) diff --git a/apps/client/src/app/pages/portfolio/allocations/allocations-page.scss b/apps/client/src/app/pages/portfolio/allocations/allocations-page.scss index 0a12304c1..253660774 100644 --- a/apps/client/src/app/pages/portfolio/allocations/allocations-page.scss +++ b/apps/client/src/app/pages/portfolio/allocations/allocations-page.scss @@ -28,4 +28,9 @@ } } } + + .mat-progress-bar { + border-radius: 0.25rem; + height: 0.5rem; + } } diff --git a/libs/common/src/lib/interfaces/portfolio-details.interface.ts b/libs/common/src/lib/interfaces/portfolio-details.interface.ts index 17430438b..cffa3ac0d 100644 --- a/libs/common/src/lib/interfaces/portfolio-details.interface.ts +++ b/libs/common/src/lib/interfaces/portfolio-details.interface.ts @@ -10,5 +10,8 @@ export interface PortfolioDetails { original: number; }; }; + filteredValueInBaseCurrency?: number; + filteredValueInPercentage: number; holdings: { [symbol: string]: PortfolioPosition }; + totalValueInBaseCurrency?: number; }