From ca8c4e6236ec8514362c1b7a89a04c5f86632304 Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Wed, 22 Jul 2026 17:17:53 +0200 Subject: [PATCH] Bugfix/handle exception in holding detail endpoint for cash positions (#7391) * Handle exception for cash positions * Update changelog --- CHANGELOG.md | 1 + .../src/app/portfolio/portfolio.service.ts | 65 +++++++++++-------- 2 files changed, 40 insertions(+), 26 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3665b66ad..1a21ab9a4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed +- Resolved an exception in the `GET api/v1/portfolio/holding/:dataSource/:symbol` endpoint for cash positions - Improved the error handling in the access endpoints (`POST` and `PUT`) to return `400 Bad Request` when granting access to a non-existent user ## 3.31.0 - 2026-07-20 diff --git a/apps/api/src/app/portfolio/portfolio.service.ts b/apps/api/src/app/portfolio/portfolio.service.ts index 6617b8f9b..b3e86e050 100644 --- a/apps/api/src/app/portfolio/portfolio.service.ts +++ b/apps/api/src/app/portfolio/portfolio.service.ts @@ -783,10 +783,24 @@ export class PortfolioService { return undefined; } - const [SymbolProfile] = await this.symbolProfileService.getSymbolProfiles([ + const [symbolProfile] = await this.symbolProfileService.getSymbolProfiles([ { dataSource, symbol } ]); + const assetProfile = + symbolProfile ?? + ({ + dataSource, + symbol, + assetClass: AssetClass.LIQUIDITY, + assetSubClass: AssetSubClass.CASH, + countries: [], + currency: symbol, + holdings: [], + name: symbol, + sectors: [] + } as EnhancedSymbolProfile); + const portfolioCalculator = this.calculatorFactory.createCalculator({ activities, userId, @@ -829,9 +843,10 @@ export class PortfolioService { timeWeightedInvestmentWithCurrencyEffect } = holding; - const activitiesOfHolding = activities.filter(({ assetProfile }) => { + const activitiesOfHolding = activities.filter((activity) => { return ( - assetProfile.dataSource === dataSource && assetProfile.symbol === symbol + activity.assetProfile.dataSource === dataSource && + activity.assetProfile.symbol === symbol ); }); @@ -863,19 +878,17 @@ export class PortfolioService { new Date() ); + const [firstActivity] = activitiesOfHolding; + const referenceUnitPrice = + firstActivity?.unitPriceInAssetProfileCurrency ?? marketPrice; + const historicalDataArray: HistoricalDataItem[] = []; - let marketPriceMax = Math.max( - activitiesOfHolding[0].unitPriceInAssetProfileCurrency, - marketPrice - ); + let marketPriceMax = Math.max(referenceUnitPrice, marketPrice); let marketPriceMaxDate = - marketPrice > activitiesOfHolding[0].unitPriceInAssetProfileCurrency + marketPrice > referenceUnitPrice ? new Date() - : activitiesOfHolding[0].date; - let marketPriceMin = Math.min( - activitiesOfHolding[0].unitPriceInAssetProfileCurrency, - marketPrice - ); + : (firstActivity?.date ?? new Date()); + let marketPriceMin = Math.min(referenceUnitPrice, marketPrice); const historicalDataItems = historicalData[getAssetProfileIdentifier({ dataSource, symbol })]; @@ -926,10 +939,10 @@ export class PortfolioService { } else { // Add historical entry for buy date, if no historical data available historicalDataArray.push({ - averagePrice: activitiesOfHolding[0].unitPriceInAssetProfileCurrency, + averagePrice: referenceUnitPrice, date: dateOfFirstActivity, - marketPrice: activitiesOfHolding[0].unitPriceInAssetProfileCurrency, - quantity: activitiesOfHolding[0].quantity + marketPrice: referenceUnitPrice, + quantity: firstActivity?.quantity ?? quantity.toNumber() }); } @@ -947,16 +960,16 @@ export class PortfolioService { marketPriceMin, tags, assetProfile: { - assetClass: SymbolProfile.assetClass, - assetSubClass: SymbolProfile.assetSubClass, - countries: SymbolProfile.countries, - currency: SymbolProfile.currency, - dataSource: SymbolProfile.dataSource, - isin: SymbolProfile.isin, - name: SymbolProfile.name, - sectors: SymbolProfile.sectors, - symbol: SymbolProfile.symbol, - userId: SymbolProfile.userId + assetClass: assetProfile.assetClass, + assetSubClass: assetProfile.assetSubClass, + countries: assetProfile.countries, + currency: assetProfile.currency, + dataSource: assetProfile.dataSource, + isin: assetProfile.isin, + name: assetProfile.name, + sectors: assetProfile.sectors, + symbol: assetProfile.symbol, + userId: assetProfile.userId }, averagePrice: averagePrice.toNumber(), dataProviderInfo: portfolioCalculator.getDataProviderInfos()?.[0],