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. 13
      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`)
- 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
### 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 { MatProgressBarModule } from '@angular/material/progress-bar';
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 { DeviceDetectorService } from 'ngx-device-detector';
import { Subject } from 'rxjs';
@ -63,6 +69,18 @@ export class GfAllocationsPageComponent implements OnDestroy, OnInit {
};
public deviceType: string;
public hasImpersonationId: boolean;
public holdings: {
[symbol: string]: Pick<
PortfolioPosition,
| 'assetClass'
| 'assetClassLabel'
| 'assetSubClass'
| 'assetSubClassLabel'
| 'currency'
| 'exchange'
| 'name'
> & { etfProvider: string; value: number };
};
public isLoading = false;
public markets: {
[key in Market]: { id: Market; valueInPercentage: number };
@ -81,18 +99,6 @@ export class GfAllocationsPageComponent implements OnDestroy, OnInit {
};
};
public portfolioDetails: PortfolioDetails;
public positions: {
[symbol: string]: Pick<
PortfolioPosition,
| 'assetClass'
| 'assetClassLabel'
| 'assetSubClass'
| 'assetSubClassLabel'
| 'currency'
| 'exchange'
| 'name'
> & { etfProvider: string; value: number };
};
public sectors: {
[name: string]: { name: string; value: number };
};
@ -237,6 +243,7 @@ export class GfAllocationsPageComponent implements OnDestroy, OnInit {
value: 0
}
};
this.holdings = {};
this.marketsAdvanced = {
[UNKNOWN_KEY]: {
id: UNKNOWN_KEY,
@ -282,7 +289,6 @@ export class GfAllocationsPageComponent implements OnDestroy, OnInit {
platforms: {},
summary: undefined
};
this.positions = {};
this.sectors = {
[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(
this.portfolioDetails.holdings
)) {
@ -340,12 +336,12 @@ export class GfAllocationsPageComponent implements OnDestroy, OnInit {
value = position.valueInBaseCurrency;
}
this.positions[symbol] = {
this.holdings[symbol] = {
value,
assetClass: position.assetClass,
assetClassLabel: position.assetClassLabel,
assetSubClass: position.assetSubClass,
assetSubClassLabel: position.assetSubClassLabel,
assetClass: position.assetClass || (UNKNOWN_KEY as AssetClass),
assetClassLabel: position.assetClassLabel || UNKNOWN_KEY,
assetSubClass: position.assetSubClass || (UNKNOWN_KEY as AssetSubClass),
assetSubClassLabel: position.assetSubClassLabel || UNKNOWN_KEY,
currency: position.currency,
etfProvider: this.extractEtfProvider({
assetSubClass: position.assetSubClass,
@ -462,8 +458,8 @@ export class GfAllocationsPageComponent implements OnDestroy, OnInit {
}
}
if (this.positions[symbol].assetSubClass === 'ETF') {
this.totalValueInEtf += this.positions[symbol].value;
if (this.holdings[symbol].assetSubClass === 'ETF') {
this.totalValueInEtf += this.holdings[symbol].value;
}
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 [
id,
{ name, valueInBaseCurrency, valueInPercentage }

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

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

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

Loading…
Cancel
Save