Browse Source

Task/improve allocations by ETF holding by refining grouping (#6619)

* Improve grouping of same assets with diverging names

* Prettify asset names

* Update changelog
pull/6614/head^2
Thomas Kaul 3 days ago
committed by GitHub
parent
commit
76355d938d
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 1
      CHANGELOG.md
  2. 30
      apps/client/src/app/pages/portfolio/allocations/allocations-page.component.ts
  3. 2
      libs/ui/src/lib/top-holdings/top-holdings.component.html
  4. 28
      libs/ui/src/lib/top-holdings/top-holdings.component.ts

1
CHANGELOG.md

@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed ### 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`) - Improved the language localization for Polish (`pl`)
- Upgraded `@trivago/prettier-plugin-sort-imports` from version `5.2.2` to `6.0.2` - Upgraded `@trivago/prettier-plugin-sort-imports` from version `5.2.2` to `6.0.2`

30
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) { if (position.holdings.length > 0) {
for (const holding of position.holdings) { for (const {
const { allocationInPercentage, name, valueInBaseCurrency } = allocationInPercentage,
holding; name,
valueInBaseCurrency
} of position.holdings) {
const normalizedAssetName = this.normalizeAssetName(name);
if (this.topHoldingsMap[name]?.value) { if (this.topHoldingsMap[normalizedAssetName]?.value) {
this.topHoldingsMap[name].value += isNumber(valueInBaseCurrency) this.topHoldingsMap[normalizedAssetName].value += isNumber(
valueInBaseCurrency
)
? valueInBaseCurrency ? valueInBaseCurrency
: allocationInPercentage * : allocationInPercentage *
this.portfolioDetails.holdings[symbol].valueInPercentage; this.portfolioDetails.holdings[symbol].valueInPercentage;
} else { } else {
this.topHoldingsMap[name] = { this.topHoldingsMap[normalizedAssetName] = {
name, name,
value: isNumber(valueInBaseCurrency) value: isNumber(valueInBaseCurrency)
? valueInBaseCurrency ? valueInBaseCurrency
@ -518,7 +523,10 @@ export class GfAllocationsPageComponent implements OnInit {
if (holding.holdings.length > 0) { if (holding.holdings.length > 0) {
const currentParentHolding = holding.holdings.find( const currentParentHolding = holding.holdings.find(
(parentHolding) => { (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) { private openAccountDetailDialog(aAccountId: string) {
const dialogRef = this.dialog.open< const dialogRef = this.dialog.open<
GfAccountDetailDialogComponent, GfAccountDetailDialogComponent,

2
libs/ui/src/lib/top-holdings/top-holdings.component.html

@ -16,7 +16,7 @@
<ng-container i18n>Name</ng-container> <ng-container i18n>Name</ng-container>
</th> </th>
<td *matCellDef="let element" class="px-2" mat-cell> <td *matCellDef="let element" class="px-2" mat-cell>
<div class="text-truncate">{{ element?.name | titlecase }}</div> <div class="text-truncate">{{ prettifyAssetName(element?.name) }}</div>
</td> </td>
</ng-container> </ng-container>

28
libs/ui/src/lib/top-holdings/top-holdings.component.ts

@ -20,15 +20,14 @@ import {
EventEmitter, EventEmitter,
Input, Input,
OnChanges, OnChanges,
OnDestroy,
Output, Output,
ViewChild ViewChild
} from '@angular/core'; } from '@angular/core';
import { MatButtonModule } from '@angular/material/button'; import { MatButtonModule } from '@angular/material/button';
import { MatPaginator, MatPaginatorModule } from '@angular/material/paginator'; import { MatPaginator, MatPaginatorModule } from '@angular/material/paginator';
import { MatTableDataSource, MatTableModule } from '@angular/material/table'; import { MatTableDataSource, MatTableModule } from '@angular/material/table';
import { capitalize } from 'lodash';
import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader'; import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader';
import { Subject } from 'rxjs';
import { GfValueComponent } from '../value/value.component'; import { GfValueComponent } from '../value/value.component';
@ -58,7 +57,7 @@ import { GfValueComponent } from '../value/value.component';
styleUrls: ['./top-holdings.component.scss'], styleUrls: ['./top-holdings.component.scss'],
templateUrl: './top-holdings.component.html' templateUrl: './top-holdings.component.html'
}) })
export class GfTopHoldingsComponent implements OnChanges, OnDestroy { export class GfTopHoldingsComponent implements OnChanges {
@Input() baseCurrency: string; @Input() baseCurrency: string;
@Input() locale = getLocale(); @Input() locale = getLocale();
@Input() pageSize = Number.MAX_SAFE_INTEGER; @Input() pageSize = Number.MAX_SAFE_INTEGER;
@ -76,8 +75,6 @@ export class GfTopHoldingsComponent implements OnChanges, OnDestroy {
]; ];
public isLoading = true; public isLoading = true;
private unsubscribeSubject = new Subject<void>();
public ngOnChanges() { public ngOnChanges() {
this.isLoading = true; this.isLoading = true;
@ -101,8 +98,23 @@ export class GfTopHoldingsComponent implements OnChanges, OnDestroy {
}); });
} }
public ngOnDestroy() { public prettifyAssetName(name: string) {
this.unsubscribeSubject.next(); if (!name) {
this.unsubscribeSubject.complete(); return '';
}
return name
.split(' ')
.filter((token) => {
return !token.startsWith('(') && !token.endsWith(')');
})
.map((token) => {
if (token.length <= 2) {
return token.toUpperCase();
}
return capitalize(token);
})
.join(' ');
} }
} }

Loading…
Cancel
Save