diff --git a/apps/api/src/app/portfolio/calculator/portfolio-calculator.ts b/apps/api/src/app/portfolio/calculator/portfolio-calculator.ts index 12f5cada2c..ff4cb64b11 100644 --- a/apps/api/src/app/portfolio/calculator/portfolio-calculator.ts +++ b/apps/api/src/app/portfolio/calculator/portfolio-calculator.ts @@ -327,6 +327,7 @@ export abstract class PortfolioCalculator { totalInvestmentValueWithCurrencyEffect: Big; totalNetPerformanceValue: Big; totalNetPerformanceValueWithCurrencyEffect: Big; + totalNetWorthValueWithCurrencyEffect: Big; totalTimeWeightedInvestmentValue: Big; totalTimeWeightedInvestmentValueWithCurrencyEffect: Big; }; @@ -341,6 +342,7 @@ export abstract class PortfolioCalculator { investmentValuesWithCurrencyEffect: { [date: string]: Big }; netPerformanceValues: { [date: string]: Big }; netPerformanceValuesWithCurrencyEffect: { [date: string]: Big }; + netWorthValuesWithCurrencyEffect: { [date: string]: Big }; timeWeightedInvestmentValues: { [date: string]: Big }; timeWeightedInvestmentValuesWithCurrencyEffect: { [date: string]: Big }; }; @@ -359,7 +361,8 @@ export abstract class PortfolioCalculator { const isCashInBaseCurrency = item.assetSubClass === AssetSubClass.CASH && - item.currency === this.currency; + item.currency === this.currency && + item.symbol === this.currency; const { currentValues, @@ -402,16 +405,19 @@ export abstract class PortfolioCalculator { hasAnySymbolMetricsErrors = hasAnySymbolMetricsErrors || hasErrors; // Cash in the base currency cannot generate a currency effect and thus - // contributes nothing but its balance to the performance calculation + // contributes nothing but its balance to the performance calculation. It + // is therefore excluded from the value and the investment, while still + // contributing to the net worth. valuesBySymbol[item.symbol] = isCashInBaseCurrency ? { - currentValues, - currentValuesWithCurrencyEffect, + currentValues: {}, + currentValuesWithCurrencyEffect: {}, investmentValuesAccumulated: {}, investmentValuesAccumulatedWithCurrencyEffect: {}, investmentValuesWithCurrencyEffect: {}, netPerformanceValues: {}, netPerformanceValuesWithCurrencyEffect: {}, + netWorthValuesWithCurrencyEffect: currentValuesWithCurrencyEffect, timeWeightedInvestmentValues: {}, timeWeightedInvestmentValuesWithCurrencyEffect: {} } @@ -424,7 +430,8 @@ export abstract class PortfolioCalculator { netPerformanceValues, netPerformanceValuesWithCurrencyEffect, timeWeightedInvestmentValues, - timeWeightedInvestmentValuesWithCurrencyEffect + timeWeightedInvestmentValuesWithCurrencyEffect, + netWorthValuesWithCurrencyEffect: currentValuesWithCurrencyEffect }; positions.push({ @@ -528,6 +535,10 @@ export abstract class PortfolioCalculator { symbolValues.netPerformanceValuesWithCurrencyEffect?.[dateString] ?? new Big(0); + const netWorthValueWithCurrencyEffect = + symbolValues.netWorthValuesWithCurrencyEffect?.[dateString] ?? + new Big(0); + const timeWeightedInvestmentValue = symbolValues.timeWeightedInvestmentValues?.[dateString] ?? new Big(0); @@ -546,7 +557,7 @@ export abstract class PortfolioCalculator { ?.totalCashValueWithCurrencyEffect ?? new Big(0) ).add( cashSymbols.has(symbol) - ? currentValueWithCurrencyEffect + ? netWorthValueWithCurrencyEffect : new Big(0) ), totalCurrentValue: ( @@ -572,6 +583,10 @@ export abstract class PortfolioCalculator { accumulatedValuesByDate[dateString] ?.totalNetPerformanceValueWithCurrencyEffect ?? new Big(0) ).add(netPerformanceValueWithCurrencyEffect), + totalNetWorthValueWithCurrencyEffect: ( + accumulatedValuesByDate[dateString] + ?.totalNetWorthValueWithCurrencyEffect ?? new Big(0) + ).add(netWorthValueWithCurrencyEffect), totalTimeWeightedInvestmentValue: ( accumulatedValuesByDate[dateString] ?.totalTimeWeightedInvestmentValue ?? new Big(0) @@ -596,6 +611,7 @@ export abstract class PortfolioCalculator { totalInvestmentValueWithCurrencyEffect, totalNetPerformanceValue, totalNetPerformanceValueWithCurrencyEffect, + totalNetWorthValueWithCurrencyEffect, totalTimeWeightedInvestmentValue, totalTimeWeightedInvestmentValueWithCurrencyEffect } = values; @@ -622,7 +638,7 @@ export abstract class PortfolioCalculator { netPerformance: totalNetPerformanceValue.toNumber(), netPerformanceWithCurrencyEffect: totalNetPerformanceValueWithCurrencyEffect.toNumber(), - netWorth: totalCurrentValueWithCurrencyEffect.toNumber(), + netWorth: totalNetWorthValueWithCurrencyEffect.toNumber(), totalCashInBaseCurrency: totalCashValueWithCurrencyEffect.toNumber(), totalInvestment: totalInvestmentValue.toNumber(), totalInvestmentValueWithCurrencyEffect: diff --git a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-cash.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-cash.spec.ts index dc22f75965..81c3191f99 100644 --- a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-cash.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-cash.spec.ts @@ -433,6 +433,11 @@ describe('PortfolioCalculator', () => { totalLiabilitiesWithCurrencyEffect: new Big(0) }); + /** + * Value: 0 CHF (the cash is excluded from the performance calculation + * and therefore from the value it is measured against) + * Net worth: 2000 CHF (the cash still counts towards the net worth) + */ expect(portfolioSnapshot.historicalData.at(-1)).toEqual({ date: '2025-01-01', investmentValueWithCurrencyEffect: 0, @@ -444,8 +449,8 @@ describe('PortfolioCalculator', () => { totalCashInBaseCurrency: 2000, totalInvestment: 0, totalInvestmentValueWithCurrencyEffect: 0, - value: 2000, - valueWithCurrencyEffect: 2000 + value: 0, + valueWithCurrencyEffect: 0 }); }); }); diff --git a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator.ts index 7214f8b445..1c6cb979cc 100644 --- a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator.ts +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator.ts @@ -41,12 +41,6 @@ export class RoaiPortfolioCalculator extends PortfolioCalculator { let totalTimeWeightedInvestmentWithCurrencyEffect = new Big(0); for (const currentPosition of positions) { - if (currentPosition.feeInBaseCurrency) { - totalFeesWithCurrencyEffect = totalFeesWithCurrencyEffect.plus( - currentPosition.feeInBaseCurrency - ); - } - if (currentPosition.valueInBaseCurrency) { currentValueInBaseCurrency = currentValueInBaseCurrency.plus( currentPosition.valueInBaseCurrency @@ -59,6 +53,12 @@ export class RoaiPortfolioCalculator extends PortfolioCalculator { continue; } + if (currentPosition.feeInBaseCurrency) { + totalFeesWithCurrencyEffect = totalFeesWithCurrencyEffect.plus( + currentPosition.feeInBaseCurrency + ); + } + if (currentPosition.investment) { totalInvestment = totalInvestment.plus(currentPosition.investment); diff --git a/apps/api/src/app/portfolio/portfolio.service.ts b/apps/api/src/app/portfolio/portfolio.service.ts index b7561605da..6ae86e4e7b 100644 --- a/apps/api/src/app/portfolio/portfolio.service.ts +++ b/apps/api/src/app/portfolio/portfolio.service.ts @@ -2040,7 +2040,7 @@ export class PortfolioService { .toNumber(), interestInBaseCurrency: interest.toNumber(), liabilitiesInBaseCurrency: liabilities.toNumber(), - totalCashInBaseCurrency: totalCashInBaseCurrency?.toNumber() ?? 0, + totalCashInBaseCurrency: balanceInBaseCurrency, totalInvestment: totalInvestment.toNumber(), totalInvestmentValueWithCurrencyEffect: totalInvestmentWithCurrencyEffect.toNumber(),