From 1770350516c05dddf817f6d727dcdb409f8a873f Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Fri, 31 Jul 2026 18:21:48 +0200 Subject: [PATCH] Bugfix/static portfolio analysis rules for portfolio with no holdings (#7466) * Fix static portfolio analysis rules for portfolio with no holdings * Update changelog --- CHANGELOG.md | 14 ++ .../src/app/portfolio/portfolio.service.ts | 208 +++++++++--------- 2 files changed, 117 insertions(+), 105 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d5c6481e3..d14f77f5e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Improved the performance of the property service by caching the properties in memory - Improved the language localization for German (`de`) +### Fixed + +- Fixed the static portfolio analysis rule for a portfolio with no holdings: _Asset Class Cluster Risks_ (Equity) +- Fixed the static portfolio analysis rule for a portfolio with no holdings: _Asset Class Cluster Risks_ (Fixed Income) +- Fixed the static portfolio analysis rule for a portfolio with no holdings: _Currency Cluster Risks_ (Investment) +- Fixed the static portfolio analysis rule for a portfolio with no holdings: _Currency Cluster Risks_ (Investment: Base Currency) +- Fixed the static portfolio analysis rule for a portfolio with no holdings: _Economic Market Cluster Risks_ (Developed Markets) +- Fixed the static portfolio analysis rule for a portfolio with no holdings: _Economic Market Cluster Risks_ (Emerging Markets) +- Fixed the static portfolio analysis rule for a portfolio with no holdings: _Regional Market Cluster Risks_ (Asia-Pacific) +- Fixed the static portfolio analysis rule for a portfolio with no holdings: _Regional Market Cluster Risks_ (Emerging Markets) +- Fixed the static portfolio analysis rule for a portfolio with no holdings: _Regional Market Cluster Risks_ (Europe) +- Fixed the static portfolio analysis rule for a portfolio with no holdings: _Regional Market Cluster Risks_ (Japan) +- Fixed the static portfolio analysis rule for a portfolio with no holdings: _Regional Market Cluster Risks_ (North America) + ## 3.37.0 - 2026-07-30 ### Added diff --git a/apps/api/src/app/portfolio/portfolio.service.ts b/apps/api/src/app/portfolio/portfolio.service.ts index 6a3c1f145..d1aff3811 100644 --- a/apps/api/src/app/portfolio/portfolio.service.ts +++ b/apps/api/src/app/portfolio/portfolio.service.ts @@ -1126,6 +1126,8 @@ export class PortfolioService { withSummary: true }); + const hasOpenHoldings = Object.keys(holdings).length > 0; + const marketsAdvancedTotalInBaseCurrency = getSum( Object.values(marketsAdvanced).map(({ valueInBaseCurrency }) => { return new Big(valueInBaseCurrency); @@ -1185,26 +1187,25 @@ export class PortfolioService { id: 'rule.currencyClusterRisk.category', languageCode: userSettings.language }), - rules: - summary.activityCount > 0 - ? await this.rulesService.evaluate( - [ - new CurrencyClusterRiskBaseCurrencyCurrentInvestment( - this.exchangeRateDataService, - this.i18nService, - Object.values(holdings), - userSettings.language - ), - new CurrencyClusterRiskCurrentInvestment( - this.exchangeRateDataService, - this.i18nService, - Object.values(holdings), - userSettings.language - ) - ], - userSettings - ) - : undefined + rules: hasOpenHoldings + ? await this.rulesService.evaluate( + [ + new CurrencyClusterRiskBaseCurrencyCurrentInvestment( + this.exchangeRateDataService, + this.i18nService, + Object.values(holdings), + userSettings.language + ), + new CurrencyClusterRiskCurrentInvestment( + this.exchangeRateDataService, + this.i18nService, + Object.values(holdings), + userSettings.language + ) + ], + userSettings + ) + : undefined }, { key: 'assetClassClusterRisk', @@ -1212,26 +1213,25 @@ export class PortfolioService { id: 'rule.assetClassClusterRisk.category', languageCode: userSettings.language }), - rules: - summary.activityCount > 0 - ? await this.rulesService.evaluate( - [ - new AssetClassClusterRiskEquity( - this.exchangeRateDataService, - this.i18nService, - userSettings.language, - Object.values(holdings) - ), - new AssetClassClusterRiskFixedIncome( - this.exchangeRateDataService, - this.i18nService, - userSettings.language, - Object.values(holdings) - ) - ], - userSettings - ) - : undefined + rules: hasOpenHoldings + ? await this.rulesService.evaluate( + [ + new AssetClassClusterRiskEquity( + this.exchangeRateDataService, + this.i18nService, + userSettings.language, + Object.values(holdings) + ), + new AssetClassClusterRiskFixedIncome( + this.exchangeRateDataService, + this.i18nService, + userSettings.language, + Object.values(holdings) + ) + ], + userSettings + ) + : undefined }, { key: 'accountClusterRisk', @@ -1266,28 +1266,27 @@ export class PortfolioService { id: 'rule.economicMarketClusterRisk.category', languageCode: userSettings.language }), - rules: - summary.activityCount > 0 - ? await this.rulesService.evaluate( - [ - new EconomicMarketClusterRiskDevelopedMarkets( - this.exchangeRateDataService, - this.i18nService, - marketsTotalInBaseCurrency, - markets.developedMarkets.valueInBaseCurrency, - userSettings.language - ), - new EconomicMarketClusterRiskEmergingMarkets( - this.exchangeRateDataService, - this.i18nService, - marketsTotalInBaseCurrency, - markets.emergingMarkets.valueInBaseCurrency, - userSettings.language - ) - ], - userSettings - ) - : undefined + rules: hasOpenHoldings + ? await this.rulesService.evaluate( + [ + new EconomicMarketClusterRiskDevelopedMarkets( + this.exchangeRateDataService, + this.i18nService, + marketsTotalInBaseCurrency, + markets.developedMarkets.valueInBaseCurrency, + userSettings.language + ), + new EconomicMarketClusterRiskEmergingMarkets( + this.exchangeRateDataService, + this.i18nService, + marketsTotalInBaseCurrency, + markets.emergingMarkets.valueInBaseCurrency, + userSettings.language + ) + ], + userSettings + ) + : undefined }, { key: 'regionalMarketClusterRisk', @@ -1295,49 +1294,48 @@ export class PortfolioService { id: 'rule.regionalMarketClusterRisk.category', languageCode: userSettings.language }), - rules: - summary.activityCount > 0 - ? await this.rulesService.evaluate( - [ - new RegionalMarketClusterRiskAsiaPacific( - this.exchangeRateDataService, - this.i18nService, - userSettings.language, - marketsAdvancedTotalInBaseCurrency, - marketsAdvanced.asiaPacific.valueInBaseCurrency - ), - new RegionalMarketClusterRiskEmergingMarkets( - this.exchangeRateDataService, - this.i18nService, - userSettings.language, - marketsAdvancedTotalInBaseCurrency, - marketsAdvanced.emergingMarkets.valueInBaseCurrency - ), - new RegionalMarketClusterRiskEurope( - this.exchangeRateDataService, - this.i18nService, - userSettings.language, - marketsAdvancedTotalInBaseCurrency, - marketsAdvanced.europe.valueInBaseCurrency - ), - new RegionalMarketClusterRiskJapan( - this.exchangeRateDataService, - this.i18nService, - userSettings.language, - marketsAdvancedTotalInBaseCurrency, - marketsAdvanced.japan.valueInBaseCurrency - ), - new RegionalMarketClusterRiskNorthAmerica( - this.exchangeRateDataService, - this.i18nService, - userSettings.language, - marketsAdvancedTotalInBaseCurrency, - marketsAdvanced.northAmerica.valueInBaseCurrency - ) - ], - userSettings - ) - : undefined + rules: hasOpenHoldings + ? await this.rulesService.evaluate( + [ + new RegionalMarketClusterRiskAsiaPacific( + this.exchangeRateDataService, + this.i18nService, + userSettings.language, + marketsAdvancedTotalInBaseCurrency, + marketsAdvanced.asiaPacific.valueInBaseCurrency + ), + new RegionalMarketClusterRiskEmergingMarkets( + this.exchangeRateDataService, + this.i18nService, + userSettings.language, + marketsAdvancedTotalInBaseCurrency, + marketsAdvanced.emergingMarkets.valueInBaseCurrency + ), + new RegionalMarketClusterRiskEurope( + this.exchangeRateDataService, + this.i18nService, + userSettings.language, + marketsAdvancedTotalInBaseCurrency, + marketsAdvanced.europe.valueInBaseCurrency + ), + new RegionalMarketClusterRiskJapan( + this.exchangeRateDataService, + this.i18nService, + userSettings.language, + marketsAdvancedTotalInBaseCurrency, + marketsAdvanced.japan.valueInBaseCurrency + ), + new RegionalMarketClusterRiskNorthAmerica( + this.exchangeRateDataService, + this.i18nService, + userSettings.language, + marketsAdvancedTotalInBaseCurrency, + marketsAdvanced.northAmerica.valueInBaseCurrency + ) + ], + userSettings + ) + : undefined }, { key: 'fees',