diff --git a/CHANGELOG.md b/CHANGELOG.md index e19467e59..0dce1cf4e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## Unreleased + +### Fixed + +- Fixed an issue in the account value calculation caused by liabilities + ## 2.61.0 - 2024-03-04 ### Changed diff --git a/apps/api/src/app/portfolio/portfolio.service.ts b/apps/api/src/app/portfolio/portfolio.service.ts index d07389049..0d14e35df 100644 --- a/apps/api/src/app/portfolio/portfolio.service.ts +++ b/apps/api/src/app/portfolio/portfolio.service.ts @@ -439,15 +439,33 @@ export class PortfolioService { portfolioItemsNow[position.symbol] = position; } - for (const item of currentPositions.positions) { - if (item.quantity.lte(0)) { + for (const { + currency, + firstBuyDate, + grossPerformance, + grossPerformanceWithCurrencyEffect, + grossPerformancePercentage, + grossPerformancePercentageWithCurrencyEffect, + investment, + marketPrice, + marketPriceInBaseCurrency, + netPerformance, + netPerformancePercentage, + netPerformancePercentageWithCurrencyEffect, + netPerformanceWithCurrencyEffect, + quantity, + symbol, + tags, + transactionCount + } of currentPositions.positions) { + if (quantity.eq(0)) { // Ignore positions without any quantity continue; } - const value = item.quantity.mul(item.marketPriceInBaseCurrency ?? 0); - const symbolProfile = symbolProfileMap[item.symbol]; - const dataProviderResponse = dataProviderResponses[item.symbol]; + const value = quantity.mul(marketPriceInBaseCurrency ?? 0); + const symbolProfile = symbolProfileMap[symbol]; + const dataProviderResponse = dataProviderResponses[symbol]; const markets: PortfolioPosition['markets'] = { [UNKNOWN_KEY]: 0, @@ -519,40 +537,39 @@ export class PortfolioService { .toNumber(); } - holdings[item.symbol] = { + holdings[symbol] = { + currency, markets, marketsAdvanced, + marketPrice, + symbol, + tags, + transactionCount, allocationInPercentage: filteredValueInBaseCurrency.eq(0) ? 0 : value.div(filteredValueInBaseCurrency).toNumber(), assetClass: symbolProfile.assetClass, assetSubClass: symbolProfile.assetSubClass, countries: symbolProfile.countries, - currency: item.currency, dataSource: symbolProfile.dataSource, - dateOfFirstActivity: parseDate(item.firstBuyDate), - grossPerformance: item.grossPerformance?.toNumber() ?? 0, - grossPerformancePercent: - item.grossPerformancePercentage?.toNumber() ?? 0, + dateOfFirstActivity: parseDate(firstBuyDate), + grossPerformance: grossPerformance?.toNumber() ?? 0, + grossPerformancePercent: grossPerformancePercentage?.toNumber() ?? 0, grossPerformancePercentWithCurrencyEffect: - item.grossPerformancePercentageWithCurrencyEffect?.toNumber() ?? 0, + grossPerformancePercentageWithCurrencyEffect?.toNumber() ?? 0, grossPerformanceWithCurrencyEffect: - item.grossPerformanceWithCurrencyEffect?.toNumber() ?? 0, - investment: item.investment.toNumber(), - marketPrice: item.marketPrice, + grossPerformanceWithCurrencyEffect?.toNumber() ?? 0, + investment: investment.toNumber(), marketState: dataProviderResponse?.marketState ?? 'delayed', name: symbolProfile.name, - netPerformance: item.netPerformance?.toNumber() ?? 0, - netPerformancePercent: item.netPerformancePercentage?.toNumber() ?? 0, + netPerformance: netPerformance?.toNumber() ?? 0, + netPerformancePercent: netPerformancePercentage?.toNumber() ?? 0, netPerformancePercentWithCurrencyEffect: - item.netPerformancePercentageWithCurrencyEffect?.toNumber() ?? 0, + netPerformancePercentageWithCurrencyEffect?.toNumber() ?? 0, netPerformanceWithCurrencyEffect: - item.netPerformanceWithCurrencyEffect?.toNumber() ?? 0, - quantity: item.quantity.toNumber(), + netPerformanceWithCurrencyEffect?.toNumber() ?? 0, + quantity: quantity.toNumber(), sectors: symbolProfile.sectors, - symbol: item.symbol, - tags: item.tags, - transactionCount: item.transactionCount, url: symbolProfile.url, valueInBaseCurrency: value.toNumber() }; @@ -1770,24 +1787,33 @@ export class PortfolioService { activityType: 'INTEREST' }).toNumber(); - const items = Object.keys(holdings) - .filter((symbol) => { - return isUUID(symbol) && holdings[symbol].dataSource === 'MANUAL'; - }) - .map((symbol) => { - return holdings[symbol].valueInBaseCurrency; - }) - .reduce( - (previous, current) => new Big(previous).plus(current), - new Big(0) - ) - .toNumber(); - - const liabilities = this.getSumOfActivityType({ - activities, - userCurrency, - activityType: 'LIABILITY' - }).toNumber(); + const items = getSum( + Object.keys(holdings) + .filter((symbol) => { + return ( + isUUID(symbol) && + holdings[symbol].dataSource === 'MANUAL' && + holdings[symbol].valueInBaseCurrency > 0 + ); + }) + .map((symbol) => { + return new Big(holdings[symbol].valueInBaseCurrency).abs(); + }) + ).toNumber(); + + const liabilities = getSum( + Object.keys(holdings) + .filter((symbol) => { + return ( + isUUID(symbol) && + holdings[symbol].dataSource === 'MANUAL' && + holdings[symbol].valueInBaseCurrency < 0 + ); + }) + .map((symbol) => { + return new Big(holdings[symbol].valueInBaseCurrency).abs(); + }) + ).toNumber(); const totalBuy = this.getSumOfActivityType({ userCurrency, @@ -1941,7 +1967,7 @@ export class PortfolioService { private async getTransactionPoints({ filters, includeDrafts = false, - types = ['BUY', 'ITEM', 'SELL'], + types = ['BUY', 'ITEM', 'LIABILITY', 'SELL'], userId, withExcludedAccounts = false }: { @@ -2076,19 +2102,10 @@ export class PortfolioService { }); for (const account of currentAccounts) { - let ordersByAccount = orders.filter(({ accountId }) => { + const ordersByAccount = orders.filter(({ accountId }) => { return accountId === account.id; }); - const ordersOfTypeItemOrLiabilityByAccount = - ordersOfTypeItemOrLiability.filter(({ accountId }) => { - return accountId === account.id; - }); - - ordersByAccount = ordersByAccount.concat( - ordersOfTypeItemOrLiabilityByAccount - ); - accounts[account.id] = { balance: account.balance, currency: account.currency, @@ -2128,8 +2145,8 @@ export class PortfolioService { } of ordersByAccount) { let currentValueOfSymbolInBaseCurrency = quantity * - portfolioItemsNow[SymbolProfile.symbol] - ?.marketPriceInBaseCurrency ?? 0; + (portfolioItemsNow[SymbolProfile.symbol]?.marketPriceInBaseCurrency ?? + 0); if (['LIABILITY', 'SELL'].includes(type)) { currentValueOfSymbolInBaseCurrency *= getFactor(type);