Browse Source

Include cash in portfolio performance

pull/7148/head
Thomas Kaul 5 days ago
parent
commit
89aae550a7
  1. 85
      apps/api/src/app/portfolio/calculator/portfolio-calculator.ts
  2. 6
      apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btceur.spec.ts
  3. 6
      apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btcusd.spec.ts
  4. 8
      apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-cash.spec.ts
  5. 8
      apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-liability.spec.ts
  6. 6
      apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-novn-buy-and-sell.spec.ts
  7. 2
      libs/common/src/lib/interfaces/historical-data-item.interface.ts

85
apps/api/src/app/portfolio/calculator/portfolio-calculator.ts

@ -202,6 +202,7 @@ export abstract class PortfolioCalculator {
};
}
const cashSymbols = new Set<string>();
const currencies: { [symbol: string]: string } = {};
const dataGatheringItems: DataGatheringItem[] = [];
let firstIndex = transactionPoints.length;
@ -315,12 +316,11 @@ export abstract class PortfolioCalculator {
const accumulatedValuesByDate: {
[date: string]: {
investmentValueWithCurrencyEffect: Big;
totalAccountBalanceWithCurrencyEffect: Big;
totalCashValueWithCurrencyEffect: Big;
totalCurrentValue: Big;
totalCurrentValueWithCurrencyEffect: Big;
totalInvestmentValue: Big;
totalInvestmentValueWithCurrencyEffect: Big;
totalLiabilitiesWithCurrencyEffect: Big;
totalNetPerformanceValue: Big;
totalNetPerformanceValueWithCurrencyEffect: Big;
totalTimeWeightedInvestmentValue: Big;
@ -351,6 +351,8 @@ export abstract class PortfolioCalculator {
] ?? 1
);
const valueInBaseCurrency = marketPriceInBaseCurrency.mul(item.quantity);
const {
currentValues,
currentValuesWithCurrencyEffect,
@ -444,15 +446,14 @@ export abstract class PortfolioCalculator {
quantity: item.quantity,
symbol: item.symbol,
tags: item.tags,
valueInBaseCurrency: new Big(marketPriceInBaseCurrency).mul(
item.quantity
)
valueInBaseCurrency
});
if (item.assetSubClass === 'CASH') {
totalCashInBaseCurrency = totalCashInBaseCurrency.plus(
marketPriceInBaseCurrency.mul(item.quantity)
);
cashSymbols.add(item.symbol);
totalCashInBaseCurrency =
totalCashInBaseCurrency.plus(valueInBaseCurrency);
}
totalInterestWithCurrencyEffect = totalInterestWithCurrencyEffect.plus(
@ -474,56 +475,7 @@ export abstract class PortfolioCalculator {
}
}
const accountBalanceItemsMap = this.accountBalanceItems.reduce(
(map, { date, value }) => {
map[date] = new Big(value);
return map;
},
{} as { [date: string]: Big }
);
const liabilityItemsMap = this.activities.reduce(
(map, { assetProfile, date, quantity, type, unitPrice }) => {
if (type === 'LIABILITY') {
const exchangeRate =
exchangeRatesByCurrency[
`${assetProfile.currency}${this.currency}`
]?.[date] ?? 1;
map[date] = (map[date] ?? new Big(0)).plus(
quantity.mul(unitPrice).mul(exchangeRate)
);
}
return map;
},
{} as { [date: string]: Big }
);
const accountBalanceMap: { [date: string]: Big } = {};
const liabilitiesMap: { [date: string]: Big } = {};
let accumulatedLiabilities = new Big(0);
let lastKnownBalance = new Big(0);
for (const dateString of chartDates) {
if (accountBalanceItemsMap[dateString] !== undefined) {
// If there's an exact balance for this date, update lastKnownBalance
lastKnownBalance = accountBalanceItemsMap[dateString];
}
// Add the most recent balance to the accountBalanceMap
accountBalanceMap[dateString] = lastKnownBalance;
if (liabilityItemsMap[dateString] !== undefined) {
accumulatedLiabilities = accumulatedLiabilities.plus(
liabilityItemsMap[dateString]
);
}
liabilitiesMap[dateString] = accumulatedLiabilities;
for (const symbol of Object.keys(valuesBySymbol)) {
const symbolValues = valuesBySymbol[symbol];
@ -566,7 +518,14 @@ export abstract class PortfolioCalculator {
accumulatedValuesByDate[dateString]
?.investmentValueWithCurrencyEffect ?? new Big(0)
).add(investmentValueWithCurrencyEffect),
totalAccountBalanceWithCurrencyEffect: accountBalanceMap[dateString],
totalCashValueWithCurrencyEffect: (
accumulatedValuesByDate[dateString]
?.totalCashValueWithCurrencyEffect ?? new Big(0)
).add(
cashSymbols.has(symbol)
? currentValueWithCurrencyEffect
: new Big(0)
),
totalCurrentValue: (
accumulatedValuesByDate[dateString]?.totalCurrentValue ?? new Big(0)
).add(currentValue),
@ -582,7 +541,6 @@ export abstract class PortfolioCalculator {
accumulatedValuesByDate[dateString]
?.totalInvestmentValueWithCurrencyEffect ?? new Big(0)
).add(investmentValueAccumulatedWithCurrencyEffect),
totalLiabilitiesWithCurrencyEffect: liabilitiesMap[dateString],
totalNetPerformanceValue: (
accumulatedValuesByDate[dateString]?.totalNetPerformanceValue ??
new Big(0)
@ -608,12 +566,11 @@ export abstract class PortfolioCalculator {
).map(([date, values]) => {
const {
investmentValueWithCurrencyEffect,
totalAccountBalanceWithCurrencyEffect,
totalCashValueWithCurrencyEffect,
totalCurrentValue,
totalCurrentValueWithCurrencyEffect,
totalInvestmentValue,
totalInvestmentValueWithCurrencyEffect,
totalLiabilitiesWithCurrencyEffect,
totalNetPerformanceValue,
totalNetPerformanceValueWithCurrencyEffect,
totalTimeWeightedInvestmentValue,
@ -642,10 +599,8 @@ export abstract class PortfolioCalculator {
netPerformance: totalNetPerformanceValue.toNumber(),
netPerformanceWithCurrencyEffect:
totalNetPerformanceValueWithCurrencyEffect.toNumber(),
netWorth: totalCurrentValueWithCurrencyEffect
.minus(totalLiabilitiesWithCurrencyEffect)
.toNumber(),
totalAccountBalance: totalAccountBalanceWithCurrencyEffect.toNumber(),
netWorth: totalCurrentValueWithCurrencyEffect.toNumber(),
totalCashInBaseCurrency: totalCashValueWithCurrencyEffect.toNumber(),
totalInvestment: totalInvestmentValue.toNumber(),
totalInvestmentValueWithCurrencyEffect:
totalInvestmentValueWithCurrencyEffect.toNumber(),

6
apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btceur.spec.ts

@ -145,7 +145,7 @@ describe('PortfolioCalculator', () => {
netPerformanceInPercentageWithCurrencyEffect: 0,
netPerformanceWithCurrencyEffect: 0,
netWorth: 0,
totalAccountBalance: 0,
totalCashInBaseCurrency: 0,
totalInvestment: 0,
totalInvestmentValueWithCurrencyEffect: 0,
value: 0,
@ -163,7 +163,7 @@ describe('PortfolioCalculator', () => {
netPerformanceInPercentageWithCurrencyEffect: 0.12422837255001412, // 5535.42 ÷ 44558.42 = 0.12422837255001412
netPerformanceWithCurrencyEffect: 5535.42,
netWorth: 50098.3, // 1 * 50098.3 = 50098.3
totalAccountBalance: 0,
totalCashInBaseCurrency: 0,
totalInvestment: 44558.42,
totalInvestmentValueWithCurrencyEffect: 44558.42,
value: 50098.3, // 1 * 50098.3 = 50098.3
@ -182,7 +182,7 @@ describe('PortfolioCalculator', () => {
netPerformanceInPercentageWithCurrencyEffect: -0.032837340282712,
netPerformanceWithCurrencyEffect: -1463.18,
netWorth: 43099.7,
totalAccountBalance: 0,
totalCashInBaseCurrency: 0,
totalInvestment: 44558.42,
totalInvestmentValueWithCurrencyEffect: 44558.42,
value: 43099.7,

6
apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btcusd.spec.ts

@ -145,7 +145,7 @@ describe('PortfolioCalculator', () => {
netPerformanceInPercentageWithCurrencyEffect: 0,
netPerformanceWithCurrencyEffect: 0,
netWorth: 0,
totalAccountBalance: 0,
totalCashInBaseCurrency: 0,
totalInvestment: 0,
totalInvestmentValueWithCurrencyEffect: 0,
value: 0,
@ -163,7 +163,7 @@ describe('PortfolioCalculator', () => {
netPerformanceInPercentageWithCurrencyEffect: 0.12422837255001412, // 5535.42 ÷ 44558.42 = 0.12422837255001412
netPerformanceWithCurrencyEffect: 5535.42, // 1 * (50098.3 - 44558.42) - 4.46 = 5535.42
netWorth: 50098.3, // 1 * 50098.3 = 50098.3
totalAccountBalance: 0,
totalCashInBaseCurrency: 0,
totalInvestment: 44558.42,
totalInvestmentValueWithCurrencyEffect: 44558.42,
value: 50098.3, // 1 * 50098.3 = 50098.3
@ -182,7 +182,7 @@ describe('PortfolioCalculator', () => {
netPerformanceInPercentageWithCurrencyEffect: -0.032837340282712,
netPerformanceWithCurrencyEffect: -1463.18,
netWorth: 43099.7,
totalAccountBalance: 0,
totalCashInBaseCurrency: 0,
totalInvestment: 44558.42,
totalInvestmentValueWithCurrencyEffect: 44558.42,
value: 43099.7,

8
apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-cash.spec.ts

@ -301,8 +301,10 @@ describe('PortfolioCalculator', () => {
/**
* Value with currency effect: 2000 USD * 0.91 = 1820 CHF
* Net worth: 1820 CHF - 0 CHF (liabilities) = 1820 CHF
* Total account balance: 1800 CHF (balance in base currency on 2024-12-31)
* Net worth: 1820 CHF (the cash is included in the value and therefore
* not added on top of it again)
* Cash in base currency: 2000 USD * 0.91 = 1820 CHF (the whole portfolio
* consists of cash, hence it matches the value)
* Net performance with currency effect: 70 CHF / 852.45 CHF 8.21 %
*/
expect(lastDataItem).toEqual({
@ -313,7 +315,7 @@ describe('PortfolioCalculator', () => {
netPerformanceInPercentageWithCurrencyEffect: 0.08211603004634808,
netPerformanceWithCurrencyEffect: 70,
netWorth: 1820,
totalAccountBalance: 1800,
totalCashInBaseCurrency: 1820,
totalInvestment: 1820,
totalInvestmentValueWithCurrencyEffect: 1750,
value: 1820,

8
apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-liability.spec.ts

@ -113,14 +113,6 @@ describe('PortfolioCalculator', () => {
expect(portfolioSnapshot.totalLiabilitiesWithCurrencyEffect).toEqual(
new Big(3000)
);
/**
* Net worth: 0 USD (current value) - 3000 USD (liabilities) = -3000 USD
*/
expect(portfolioSnapshot.historicalData.at(-1)).toMatchObject({
netWorth: -3000,
valueWithCurrencyEffect: 0
});
});
});
});

6
apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-novn-buy-and-sell.spec.ts

@ -142,7 +142,7 @@ describe('PortfolioCalculator', () => {
netPerformanceInPercentageWithCurrencyEffect: 0,
netPerformanceWithCurrencyEffect: 0,
netWorth: 0,
totalAccountBalance: 0,
totalCashInBaseCurrency: 0,
totalInvestment: 0,
totalInvestmentValueWithCurrencyEffect: 0,
value: 0,
@ -161,7 +161,7 @@ describe('PortfolioCalculator', () => {
netPerformanceInPercentageWithCurrencyEffect: 0.158311345646438, // 24 ÷ 151.6 = 0.158311345646438
netPerformanceWithCurrencyEffect: 24,
netWorth: 175.6, // 2 * 87.8 = 175.6
totalAccountBalance: 0,
totalCashInBaseCurrency: 0,
totalInvestment: 151.6,
totalInvestmentValueWithCurrencyEffect: 151.6,
value: 175.6, // 2 * 87.8 = 175.6
@ -180,7 +180,7 @@ describe('PortfolioCalculator', () => {
netPerformanceInPercentageWithCurrencyEffect: 0.13100263852242744,
netPerformanceWithCurrencyEffect: 19.86,
netWorth: 0,
totalAccountBalance: 0,
totalCashInBaseCurrency: 0,
totalInvestment: 0,
totalInvestmentValueWithCurrencyEffect: 0,
value: 0,

2
libs/common/src/lib/interfaces/historical-data-item.interface.ts

@ -11,7 +11,7 @@ export interface HistoricalDataItem {
netWorth?: number;
netWorthInPercentage?: number;
quantity?: number;
totalAccountBalance?: number;
totalCashInBaseCurrency?: number;
totalInvestment?: number;
totalInvestmentValueWithCurrencyEffect?: number;
value?: number;

Loading…
Cancel
Save