diff --git a/CHANGELOG.md b/CHANGELOG.md index 7ebec12b7..93142484a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- Improved the allocations by ETF holding on the allocations page (experimental) +- Improved the language localization for German (`de`) - Upgraded `prisma` from version `5.14.0` to `5.15.0` ## 2.86.0 - 2024-06-07 diff --git a/apps/api/src/app/portfolio/portfolio.controller.ts b/apps/api/src/app/portfolio/portfolio.controller.ts index 5cdaa1641..19f48fa86 100644 --- a/apps/api/src/app/portfolio/portfolio.controller.ts +++ b/apps/api/src/app/portfolio/portfolio.controller.ts @@ -204,6 +204,7 @@ export class PortfolioController { : undefined, countries: hasDetails ? portfolioPosition.countries : [], currency: hasDetails ? portfolioPosition.currency : undefined, + holdings: hasDetails ? portfolioPosition.holdings : [], markets: hasDetails ? portfolioPosition.markets : undefined, marketsAdvanced: hasDetails ? portfolioPosition.marketsAdvanced diff --git a/apps/api/src/services/data-provider/data-enhancer/trackinsight/trackinsight.service.ts b/apps/api/src/services/data-provider/data-enhancer/trackinsight/trackinsight.service.ts index 284caed4f..0e12b8f02 100644 --- a/apps/api/src/services/data-provider/data-enhancer/trackinsight/trackinsight.service.ts +++ b/apps/api/src/services/data-provider/data-enhancer/trackinsight/trackinsight.service.ts @@ -163,6 +163,10 @@ export class TrackinsightDataEnhancerService implements DataEnhancerInterface { response.holdings = []; for (const { label, weight } of holdings?.topHoldings ?? []) { + if (label?.toLowerCase() === 'other') { + continue; + } + response.holdings.push({ weight, name: label diff --git a/apps/client/src/app/pages/portfolio/allocations/allocations-page.component.ts b/apps/client/src/app/pages/portfolio/allocations/allocations-page.component.ts index f8ad447de..18c839d10 100644 --- a/apps/client/src/app/pages/portfolio/allocations/allocations-page.component.ts +++ b/apps/client/src/app/pages/portfolio/allocations/allocations-page.component.ts @@ -3,7 +3,7 @@ import { AccountDetailDialogParams } from '@ghostfolio/client/components/account 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'; -import { UNKNOWN_KEY } from '@ghostfolio/common/config'; +import { MAX_TOP_HOLDINGS, UNKNOWN_KEY } from '@ghostfolio/common/config'; import { prettifySymbol } from '@ghostfolio/common/helper'; import { Holding, @@ -85,7 +85,7 @@ export class AllocationsPageComponent implements OnDestroy, OnInit { value: number; }; }; - public topHoldings: Holding[] = []; + public topHoldings: Holding[]; public topHoldingsMap: { [name: string]: { name: string; value: number }; }; @@ -456,21 +456,28 @@ export class AllocationsPageComponent implements OnDestroy, OnInit { for (const holding of position.holdings) { const { name, valueInBaseCurrency } = holding; - if (this.topHoldingsMap[name]?.value) { - this.topHoldingsMap[name].value += - valueInBaseCurrency * - (isNumber(position.valueInBaseCurrency) - ? position.valueInBaseCurrency - : position.valueInPercentage); - } else { - this.topHoldingsMap[name] = { - name, - value: + if ( + !this.hasImpersonationId && + !this.user.settings.isRestrictedView + ) { + if (this.topHoldingsMap[name]?.value) { + this.topHoldingsMap[name].value += valueInBaseCurrency * (isNumber(position.valueInBaseCurrency) - ? this.portfolioDetails.holdings[symbol].valueInBaseCurrency - : this.portfolioDetails.holdings[symbol].valueInPercentage) - }; + ? position.valueInBaseCurrency + : position.valueInPercentage); + } else { + this.topHoldingsMap[name] = { + name, + value: + valueInBaseCurrency * + (isNumber(position.valueInBaseCurrency) + ? this.portfolioDetails.holdings[symbol] + .valueInBaseCurrency + : this.portfolioDetails.holdings[symbol] + .valueInPercentage) + }; + } } } } @@ -505,11 +512,7 @@ export class AllocationsPageComponent implements OnDestroy, OnInit { } } - if ( - this.positions[symbol].assetSubClass === 'ETF' && - !this.hasImpersonationId && - !this.user.settings.isRestrictedView - ) { + if (this.positions[symbol].assetSubClass === 'ETF') { this.totalValueInEtf += this.positions[symbol].value; } @@ -557,19 +560,21 @@ export class AllocationsPageComponent implements OnDestroy, OnInit { this.markets[UNKNOWN_KEY].value = this.markets[UNKNOWN_KEY].value / marketsTotal; - if (!this.hasImpersonationId && !this.user.settings.isRestrictedView) { - this.topHoldings = Object.values(this.topHoldingsMap) - .map(({ name, value }) => { - return { - name, - allocationInPercentage: - this.totalValueInEtf > 0 ? value / this.totalValueInEtf : 0, - valueInBaseCurrency: value - }; - }) - .sort((a, b) => { - return b.valueInBaseCurrency - a.valueInBaseCurrency; - }); + this.topHoldings = Object.values(this.topHoldingsMap) + .map(({ name, value }) => { + return { + name, + allocationInPercentage: + this.totalValueInEtf > 0 ? value / this.totalValueInEtf : 0, + valueInBaseCurrency: value + }; + }) + .sort((a, b) => { + return b.valueInBaseCurrency - a.valueInBaseCurrency; + }); + + if (this.topHoldings.length > MAX_TOP_HOLDINGS) { + this.topHoldings = this.topHoldings.slice(0, MAX_TOP_HOLDINGS); } } 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 3a464b2f7..bf26e1579 100644 --- a/apps/client/src/app/pages/portfolio/allocations/allocations-page.html +++ b/apps/client/src/app/pages/portfolio/allocations/allocations-page.html @@ -264,6 +264,30 @@