diff --git a/CHANGELOG.md b/CHANGELOG.md index 46beae7ce..d161599dc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ 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 by refining the grouping of the same assets with diverging names (experimental) - Improved the language localization for Polish (`pl`) - Upgraded `@trivago/prettier-plugin-sort-imports` from version `5.2.2` to `6.0.2` 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 5226c3c12..367716d2d 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 @@ -405,17 +405,22 @@ export class GfAllocationsPageComponent implements OnInit { } if (position.holdings.length > 0) { - for (const holding of position.holdings) { - const { allocationInPercentage, name, valueInBaseCurrency } = - holding; - - if (this.topHoldingsMap[name]?.value) { - this.topHoldingsMap[name].value += isNumber(valueInBaseCurrency) + for (const { + allocationInPercentage, + name, + valueInBaseCurrency + } of position.holdings) { + const normalizedAssetName = this.normalizeAssetName(name); + + if (this.topHoldingsMap[normalizedAssetName]?.value) { + this.topHoldingsMap[normalizedAssetName].value += isNumber( + valueInBaseCurrency + ) ? valueInBaseCurrency : allocationInPercentage * this.portfolioDetails.holdings[symbol].valueInPercentage; } else { - this.topHoldingsMap[name] = { + this.topHoldingsMap[normalizedAssetName] = { name, value: isNumber(valueInBaseCurrency) ? valueInBaseCurrency @@ -518,7 +523,10 @@ export class GfAllocationsPageComponent implements OnInit { if (holding.holdings.length > 0) { const currentParentHolding = holding.holdings.find( (parentHolding) => { - return parentHolding.name === name; + return ( + this.normalizeAssetName(parentHolding.name) === + this.normalizeAssetName(name) + ); } ); @@ -555,6 +563,14 @@ export class GfAllocationsPageComponent implements OnInit { } } + private normalizeAssetName(name: string) { + if (!name) { + return ''; + } + + return name.trim().toLowerCase(); + } + private openAccountDetailDialog(aAccountId: string) { const dialogRef = this.dialog.open< GfAccountDetailDialogComponent, diff --git a/libs/ui/src/lib/top-holdings/top-holdings.component.html b/libs/ui/src/lib/top-holdings/top-holdings.component.html index 7a2a84126..6ff4ecf5b 100644 --- a/libs/ui/src/lib/top-holdings/top-holdings.component.html +++ b/libs/ui/src/lib/top-holdings/top-holdings.component.html @@ -16,7 +16,7 @@ Name -
{{ element?.name | titlecase }}
+
{{ prettifyAssetName(element?.name) }}
diff --git a/libs/ui/src/lib/top-holdings/top-holdings.component.ts b/libs/ui/src/lib/top-holdings/top-holdings.component.ts index 75a96fc5c..7c9ae033f 100644 --- a/libs/ui/src/lib/top-holdings/top-holdings.component.ts +++ b/libs/ui/src/lib/top-holdings/top-holdings.component.ts @@ -20,15 +20,14 @@ import { EventEmitter, Input, OnChanges, - OnDestroy, Output, ViewChild } from '@angular/core'; import { MatButtonModule } from '@angular/material/button'; import { MatPaginator, MatPaginatorModule } from '@angular/material/paginator'; import { MatTableDataSource, MatTableModule } from '@angular/material/table'; +import { capitalize } from 'lodash'; import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader'; -import { Subject } from 'rxjs'; import { GfValueComponent } from '../value/value.component'; @@ -58,7 +57,7 @@ import { GfValueComponent } from '../value/value.component'; styleUrls: ['./top-holdings.component.scss'], templateUrl: './top-holdings.component.html' }) -export class GfTopHoldingsComponent implements OnChanges, OnDestroy { +export class GfTopHoldingsComponent implements OnChanges { @Input() baseCurrency: string; @Input() locale = getLocale(); @Input() pageSize = Number.MAX_SAFE_INTEGER; @@ -76,8 +75,6 @@ export class GfTopHoldingsComponent implements OnChanges, OnDestroy { ]; public isLoading = true; - private unsubscribeSubject = new Subject(); - public ngOnChanges() { this.isLoading = true; @@ -101,8 +98,23 @@ export class GfTopHoldingsComponent implements OnChanges, OnDestroy { }); } - public ngOnDestroy() { - this.unsubscribeSubject.next(); - this.unsubscribeSubject.complete(); + public prettifyAssetName(name: string) { + if (!name) { + return ''; + } + + return name + .split(' ') + .filter((token) => { + return !token.startsWith('(') && !token.endsWith(')'); + }) + .map((token) => { + if (token.length <= 2) { + return token.toUpperCase(); + } + + return capitalize(token); + }) + .join(' '); } }