Browse Source

Bugfix/fix allocations by asset class for unknown asset classes (#5120)

* Fix allocations by asset class

* Update changelog
pull/5123/head
Thomas Kaul 2 weeks ago
committed by GitHub
parent
commit
a94781537d
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 4
      CHANGELOG.md
  2. 68
      apps/client/src/app/pages/portfolio/allocations/allocations-page.component.ts
  3. 6
      apps/client/src/app/pages/portfolio/allocations/allocations-page.html
  4. 19
      libs/ui/src/lib/portfolio-proportion-chart/portfolio-proportion-chart.component.ts

4
CHANGELOG.md

@ -18,6 +18,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Improved the language localization for German (`de`) - Improved the language localization for German (`de`)
- Upgraded `ionicons` from version `7.4.0` to `8.0.10` - Upgraded `ionicons` from version `7.4.0` to `8.0.10`
### Fixed
- Fixed the allocations by asset class for unknown asset classes on the allocations page
## 2.178.0 - 2025-07-05 ## 2.178.0 - 2025-07-05
### Changed ### Changed

68
apps/client/src/app/pages/portfolio/allocations/allocations-page.component.ts

@ -27,7 +27,13 @@ import { MatCardModule } from '@angular/material/card';
import { MatDialog } from '@angular/material/dialog'; import { MatDialog } from '@angular/material/dialog';
import { MatProgressBarModule } from '@angular/material/progress-bar'; import { MatProgressBarModule } from '@angular/material/progress-bar';
import { ActivatedRoute, Router } from '@angular/router'; import { ActivatedRoute, Router } from '@angular/router';
import { Account, AssetClass, DataSource, Platform } from '@prisma/client'; import {
Account,
AssetClass,
AssetSubClass,
DataSource,
Platform
} from '@prisma/client';
import { isNumber } from 'lodash'; import { isNumber } from 'lodash';
import { DeviceDetectorService } from 'ngx-device-detector'; import { DeviceDetectorService } from 'ngx-device-detector';
import { Subject } from 'rxjs'; import { Subject } from 'rxjs';
@ -63,6 +69,18 @@ export class GfAllocationsPageComponent implements OnDestroy, OnInit {
}; };
public deviceType: string; public deviceType: string;
public hasImpersonationId: boolean; public hasImpersonationId: boolean;
public holdings: {
[symbol: string]: Pick<
PortfolioPosition,
| 'assetClass'
| 'assetClassLabel'
| 'assetSubClass'
| 'assetSubClassLabel'
| 'currency'
| 'exchange'
| 'name'
> & { etfProvider: string; value: number };
};
public isLoading = false; public isLoading = false;
public markets: { public markets: {
[key in Market]: { id: Market; valueInPercentage: number }; [key in Market]: { id: Market; valueInPercentage: number };
@ -81,18 +99,6 @@ export class GfAllocationsPageComponent implements OnDestroy, OnInit {
}; };
}; };
public portfolioDetails: PortfolioDetails; public portfolioDetails: PortfolioDetails;
public positions: {
[symbol: string]: Pick<
PortfolioPosition,
| 'assetClass'
| 'assetClassLabel'
| 'assetSubClass'
| 'assetSubClassLabel'
| 'currency'
| 'exchange'
| 'name'
> & { etfProvider: string; value: number };
};
public sectors: { public sectors: {
[name: string]: { name: string; value: number }; [name: string]: { name: string; value: number };
}; };
@ -237,6 +243,7 @@ export class GfAllocationsPageComponent implements OnDestroy, OnInit {
value: 0 value: 0
} }
}; };
this.holdings = {};
this.marketsAdvanced = { this.marketsAdvanced = {
[UNKNOWN_KEY]: { [UNKNOWN_KEY]: {
id: UNKNOWN_KEY, id: UNKNOWN_KEY,
@ -282,7 +289,6 @@ export class GfAllocationsPageComponent implements OnDestroy, OnInit {
platforms: {}, platforms: {},
summary: undefined summary: undefined
}; };
this.positions = {};
this.sectors = { this.sectors = {
[UNKNOWN_KEY]: { [UNKNOWN_KEY]: {
name: UNKNOWN_KEY, name: UNKNOWN_KEY,
@ -319,16 +325,6 @@ export class GfAllocationsPageComponent implements OnDestroy, OnInit {
}; };
} }
this.markets = this.portfolioDetails.markets;
Object.values(this.portfolioDetails.marketsAdvanced).forEach(
({ id, valueInBaseCurrency, valueInPercentage }) => {
this.marketsAdvanced[id].value = isNumber(valueInBaseCurrency)
? valueInBaseCurrency
: valueInPercentage;
}
);
for (const [symbol, position] of Object.entries( for (const [symbol, position] of Object.entries(
this.portfolioDetails.holdings this.portfolioDetails.holdings
)) { )) {
@ -340,12 +336,12 @@ export class GfAllocationsPageComponent implements OnDestroy, OnInit {
value = position.valueInBaseCurrency; value = position.valueInBaseCurrency;
} }
this.positions[symbol] = { this.holdings[symbol] = {
value, value,
assetClass: position.assetClass, assetClass: position.assetClass || (UNKNOWN_KEY as AssetClass),
assetClassLabel: position.assetClassLabel, assetClassLabel: position.assetClassLabel || UNKNOWN_KEY,
assetSubClass: position.assetSubClass, assetSubClass: position.assetSubClass || (UNKNOWN_KEY as AssetSubClass),
assetSubClassLabel: position.assetSubClassLabel, assetSubClassLabel: position.assetSubClassLabel || UNKNOWN_KEY,
currency: position.currency, currency: position.currency,
etfProvider: this.extractEtfProvider({ etfProvider: this.extractEtfProvider({
assetSubClass: position.assetSubClass, assetSubClass: position.assetSubClass,
@ -462,8 +458,8 @@ export class GfAllocationsPageComponent implements OnDestroy, OnInit {
} }
} }
if (this.positions[symbol].assetSubClass === 'ETF') { if (this.holdings[symbol].assetSubClass === 'ETF') {
this.totalValueInEtf += this.positions[symbol].value; this.totalValueInEtf += this.holdings[symbol].value;
} }
this.symbols[prettifySymbol(symbol)] = { this.symbols[prettifySymbol(symbol)] = {
@ -476,6 +472,16 @@ export class GfAllocationsPageComponent implements OnDestroy, OnInit {
}; };
} }
this.markets = this.portfolioDetails.markets;
Object.values(this.portfolioDetails.marketsAdvanced).forEach(
({ id, valueInBaseCurrency, valueInPercentage }) => {
this.marketsAdvanced[id].value = isNumber(valueInBaseCurrency)
? valueInBaseCurrency
: valueInPercentage;
}
);
for (const [ for (const [
id, id,
{ name, valueInBaseCurrency, valueInPercentage } { name, valueInBaseCurrency, valueInPercentage }

6
apps/client/src/app/pages/portfolio/allocations/allocations-page.html

@ -70,7 +70,7 @@
<gf-portfolio-proportion-chart <gf-portfolio-proportion-chart
[baseCurrency]="user?.settings?.baseCurrency" [baseCurrency]="user?.settings?.baseCurrency"
[colorScheme]="user?.settings?.colorScheme" [colorScheme]="user?.settings?.colorScheme"
[data]="positions" [data]="holdings"
[isInPercent]="hasImpersonationId || user.settings.isRestrictedView" [isInPercent]="hasImpersonationId || user.settings.isRestrictedView"
[keys]="['currency']" [keys]="['currency']"
[locale]="user?.settings?.locale" [locale]="user?.settings?.locale"
@ -92,7 +92,7 @@
<gf-portfolio-proportion-chart <gf-portfolio-proportion-chart
[baseCurrency]="user?.settings?.baseCurrency" [baseCurrency]="user?.settings?.baseCurrency"
[colorScheme]="user?.settings?.colorScheme" [colorScheme]="user?.settings?.colorScheme"
[data]="positions" [data]="holdings"
[isInPercent]="hasImpersonationId || user.settings.isRestrictedView" [isInPercent]="hasImpersonationId || user.settings.isRestrictedView"
[keys]="['assetClassLabel', 'assetSubClassLabel']" [keys]="['assetClassLabel', 'assetSubClassLabel']"
[locale]="user?.settings?.locale" [locale]="user?.settings?.locale"
@ -313,7 +313,7 @@
<gf-portfolio-proportion-chart <gf-portfolio-proportion-chart
[baseCurrency]="user?.settings?.baseCurrency" [baseCurrency]="user?.settings?.baseCurrency"
[colorScheme]="user?.settings?.colorScheme" [colorScheme]="user?.settings?.colorScheme"
[data]="positions" [data]="holdings"
[isInPercent]="hasImpersonationId || user.settings.isRestrictedView" [isInPercent]="hasImpersonationId || user.settings.isRestrictedView"
[keys]="['etfProvider']" [keys]="['etfProvider']"
[locale]="user?.settings?.locale" [locale]="user?.settings?.locale"

19
libs/ui/src/lib/portfolio-proportion-chart/portfolio-proportion-chart.component.ts

@ -29,6 +29,7 @@ import { ArcElement } from 'chart.js';
import { DoughnutController } from 'chart.js'; import { DoughnutController } from 'chart.js';
import { Chart } from 'chart.js'; import { Chart } from 'chart.js';
import ChartDataLabels from 'chartjs-plugin-datalabels'; import ChartDataLabels from 'chartjs-plugin-datalabels';
import { isUUID } from 'class-validator';
import Color from 'color'; import Color from 'color';
import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader'; import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader';
@ -245,9 +246,13 @@ export class GfPortfolioProportionChartComponent
let lightnessRatio = 0.2; let lightnessRatio = 0.2;
Object.keys(item.subCategory ?? {}).forEach((subCategory) => { Object.keys(item.subCategory ?? {}).forEach((subCategory) => {
backgroundColorSubCategory.push( if (item.name === UNKNOWN_KEY) {
Color(item.color).lighten(lightnessRatio).hex() backgroundColorSubCategory.push(item.color);
); } else {
backgroundColorSubCategory.push(
Color(item.color).lighten(lightnessRatio).hex()
);
}
dataSubCategory.push(item.subCategory[subCategory].value.toNumber()); dataSubCategory.push(item.subCategory[subCategory].value.toNumber());
labelSubCategory.push(subCategory); labelSubCategory.push(subCategory);
@ -344,8 +349,14 @@ export class GfPortfolioProportionChartComponent
align: 'end', align: 'end',
anchor: 'end', anchor: 'end',
formatter: (value, context) => { formatter: (value, context) => {
const symbol = context.chart.data.labels?.[
context.dataIndex
] as string;
return value > 0 return value > 0
? context.chart.data.labels?.[context.dataIndex] ? isUUID(symbol)
? (translate(this.data[symbol]?.name) ?? symbol)
: symbol
: ''; : '';
}, },
offset: 8 offset: 8

Loading…
Cancel
Save