Browse Source

Exclude cash in base currency from portfolio performance calculation

pull/7443/head
Thomas Kaul 1 month ago
parent
commit
278aca35c3
  1. 30
      apps/api/src/app/portfolio/calculator/portfolio-calculator.ts
  2. 9
      apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-cash.spec.ts
  3. 12
      apps/api/src/app/portfolio/calculator/roai/portfolio-calculator.ts
  4. 2
      apps/api/src/app/portfolio/portfolio.service.ts

30
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:

9
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
});
});
});

12
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);

2
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(),

Loading…
Cancel
Save