From 39bd4a349bbdf1d457a119b53263028ce020c33c Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Sun, 21 Apr 2024 17:11:53 +0200 Subject: [PATCH] Feature/improve chart in account detail dialog (#3314) * Improve net worth calculation in portfolio performance chart * Improve account balance management * Update changelog --- CHANGELOG.md | 7 + apps/api/src/app/account/account.service.ts | 31 ++- .../portfolio-calculator.factory.ts | 5 + .../calculator/portfolio-calculator.ts | 178 +++++++++++++----- .../portfolio-calculator-no-orders.spec.ts | 7 +- ...folio-calculator-novn-buy-and-sell.spec.ts | 4 + .../calculator/twr/portfolio-calculator.ts | 4 + .../src/app/portfolio/portfolio.service.ts | 84 ++------- .../account-detail-dialog.component.ts | 108 ++++++----- .../pages/accounts/accounts-page.component.ts | 2 + .../interfaces/symbol-metrics.interface.ts | 1 + 11 files changed, 245 insertions(+), 186 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d4b1e2c0c..7bb4dc05a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,13 @@ 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 + +### Changed + +- Improved the chart in the account detail dialog +- Improved the account balance management + ## 2.75.0 - 2024-04-21 ### Added diff --git a/apps/api/src/app/account/account.service.ts b/apps/api/src/app/account/account.service.ts index fed1860cd..6be7e8ffb 100644 --- a/apps/api/src/app/account/account.service.ts +++ b/apps/api/src/app/account/account.service.ts @@ -1,12 +1,13 @@ import { AccountBalanceService } from '@ghostfolio/api/app/account-balance/account-balance.service'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; import { PrismaService } from '@ghostfolio/api/services/prisma/prisma.service'; +import { DATE_FORMAT } from '@ghostfolio/common/helper'; import { Filter } from '@ghostfolio/common/interfaces'; import { Injectable } from '@nestjs/common'; import { Account, Order, Platform, Prisma } from '@prisma/client'; import { Big } from 'big.js'; -import { parseISO } from 'date-fns'; +import { format } from 'date-fns'; import { groupBy } from 'lodash'; import { CashDetails } from './interfaces/cash-details.interface'; @@ -86,15 +87,11 @@ export class AccountService { data }); - await this.prismaService.accountBalance.create({ - data: { - Account: { - connect: { - id_userId: { id: account.id, userId: aUserId } - } - }, - value: data.balance - } + await this.accountBalanceService.createOrUpdateAccountBalance({ + accountId: account.id, + balance: data.balance, + date: format(new Date(), DATE_FORMAT), + userId: aUserId }); return account; @@ -197,15 +194,11 @@ export class AccountService { ): Promise { const { data, where } = params; - await this.prismaService.accountBalance.create({ - data: { - Account: { - connect: { - id_userId: where.id_userId - } - }, - value: data.balance - } + await this.accountBalanceService.createOrUpdateAccountBalance({ + accountId: data.id, + balance: data.balance, + date: format(new Date(), DATE_FORMAT), + userId: aUserId }); return this.prismaService.account.update({ diff --git a/apps/api/src/app/portfolio/calculator/portfolio-calculator.factory.ts b/apps/api/src/app/portfolio/calculator/portfolio-calculator.factory.ts index e64c23942..a75ce9b62 100644 --- a/apps/api/src/app/portfolio/calculator/portfolio-calculator.factory.ts +++ b/apps/api/src/app/portfolio/calculator/portfolio-calculator.factory.ts @@ -1,6 +1,7 @@ import { Activity } from '@ghostfolio/api/app/order/interfaces/activities.interface'; import { CurrentRateService } from '@ghostfolio/api/app/portfolio/current-rate.service'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; +import { HistoricalDataItem } from '@ghostfolio/common/interfaces'; import { DateRange } from '@ghostfolio/common/types'; import { Injectable } from '@nestjs/common'; @@ -22,11 +23,13 @@ export class PortfolioCalculatorFactory { ) {} public createCalculator({ + accountBalanceItems = [], activities, calculationType, currency, dateRange = 'max' }: { + accountBalanceItems?: HistoricalDataItem[]; activities: Activity[]; calculationType: PerformanceCalculationType; currency: string; @@ -35,6 +38,7 @@ export class PortfolioCalculatorFactory { switch (calculationType) { case PerformanceCalculationType.MWR: return new MWRPortfolioCalculator({ + accountBalanceItems, activities, currency, dateRange, @@ -43,6 +47,7 @@ export class PortfolioCalculatorFactory { }); case PerformanceCalculationType.TWR: return new TWRPortfolioCalculator({ + accountBalanceItems, activities, currency, currentRateService: this.currentRateService, diff --git a/apps/api/src/app/portfolio/calculator/portfolio-calculator.ts b/apps/api/src/app/portfolio/calculator/portfolio-calculator.ts index 1d2eadfbf..54e474779 100644 --- a/apps/api/src/app/portfolio/calculator/portfolio-calculator.ts +++ b/apps/api/src/app/portfolio/calculator/portfolio-calculator.ts @@ -37,13 +37,15 @@ import { isBefore, isSameDay, max, + min, subDays } from 'date-fns'; -import { last, uniq, uniqBy } from 'lodash'; +import { first, last, uniq, uniqBy } from 'lodash'; export abstract class PortfolioCalculator { protected static readonly ENABLE_LOGGING = false; + protected accountBalanceItems: HistoricalDataItem[]; protected orders: PortfolioOrder[]; private currency: string; @@ -57,18 +59,21 @@ export abstract class PortfolioCalculator { private transactionPoints: TransactionPoint[]; public constructor({ + accountBalanceItems, activities, currency, currentRateService, dateRange, exchangeRateDataService }: { + accountBalanceItems: HistoricalDataItem[]; activities: Activity[]; currency: string; currentRateService: CurrentRateService; dateRange: DateRange; exchangeRateDataService: ExchangeRateDataService; }) { + this.accountBalanceItems = accountBalanceItems; this.currency = currency; this.currentRateService = currentRateService; this.exchangeRateDataService = exchangeRateDataService; @@ -383,10 +388,6 @@ export abstract class PortfolioCalculator { dateRange?: DateRange; withDataDecimation?: boolean; }): Promise { - if (this.getTransactionPoints().length === 0) { - return []; - } - const { endDate, startDate } = getInterval(dateRange, this.getStartDate()); const daysInMarket = differenceInDays(endDate, startDate) + 1; @@ -485,6 +486,7 @@ export abstract class PortfolioCalculator { investmentValueWithCurrencyEffect: Big; totalCurrentValue: Big; totalCurrentValueWithCurrencyEffect: Big; + totalAccountBalanceWithCurrencyEffect: Big; totalInvestmentValue: Big; totalInvestmentValueWithCurrencyEffect: Big; totalNetPerformanceValue: Big; @@ -544,9 +546,24 @@ export abstract class PortfolioCalculator { }; } + let lastDate = format(this.startDate, DATE_FORMAT); + for (const currentDate of dates) { const dateString = format(currentDate, DATE_FORMAT); + accumulatedValuesByDate[dateString] = { + investmentValueWithCurrencyEffect: new Big(0), + totalAccountBalanceWithCurrencyEffect: new Big(0), + totalCurrentValue: new Big(0), + totalCurrentValueWithCurrencyEffect: new Big(0), + totalInvestmentValue: new Big(0), + totalInvestmentValueWithCurrencyEffect: new Big(0), + totalNetPerformanceValue: new Big(0), + totalNetPerformanceValueWithCurrencyEffect: new Big(0), + totalTimeWeightedInvestmentValue: new Big(0), + totalTimeWeightedInvestmentValueWithCurrencyEffect: new Big(0) + }; + for (const symbol of Object.keys(valuesBySymbol)) { const symbolValues = valuesBySymbol[symbol]; @@ -584,49 +601,94 @@ export abstract class PortfolioCalculator { dateString ] ?? new Big(0); - accumulatedValuesByDate[dateString] = { - investmentValueWithCurrencyEffect: ( - accumulatedValuesByDate[dateString] - ?.investmentValueWithCurrencyEffect ?? new Big(0) - ).add(investmentValueWithCurrencyEffect), - totalCurrentValue: ( - accumulatedValuesByDate[dateString]?.totalCurrentValue ?? new Big(0) - ).add(currentValue), - totalCurrentValueWithCurrencyEffect: ( - accumulatedValuesByDate[dateString] - ?.totalCurrentValueWithCurrencyEffect ?? new Big(0) - ).add(currentValueWithCurrencyEffect), - totalInvestmentValue: ( - accumulatedValuesByDate[dateString]?.totalInvestmentValue ?? - new Big(0) - ).add(investmentValueAccumulated), - totalInvestmentValueWithCurrencyEffect: ( - accumulatedValuesByDate[dateString] - ?.totalInvestmentValueWithCurrencyEffect ?? new Big(0) - ).add(investmentValueAccumulatedWithCurrencyEffect), - totalNetPerformanceValue: ( - accumulatedValuesByDate[dateString]?.totalNetPerformanceValue ?? - new Big(0) - ).add(netPerformanceValue), - totalNetPerformanceValueWithCurrencyEffect: ( - accumulatedValuesByDate[dateString] - ?.totalNetPerformanceValueWithCurrencyEffect ?? new Big(0) - ).add(netPerformanceValueWithCurrencyEffect), - totalTimeWeightedInvestmentValue: ( - accumulatedValuesByDate[dateString] - ?.totalTimeWeightedInvestmentValue ?? new Big(0) - ).add(timeWeightedInvestmentValue), - totalTimeWeightedInvestmentValueWithCurrencyEffect: ( - accumulatedValuesByDate[dateString] - ?.totalTimeWeightedInvestmentValueWithCurrencyEffect ?? new Big(0) - ).add(timeWeightedInvestmentValueWithCurrencyEffect) - }; + accumulatedValuesByDate[dateString].investmentValueWithCurrencyEffect = + accumulatedValuesByDate[ + dateString + ].investmentValueWithCurrencyEffect.add( + investmentValueWithCurrencyEffect + ); + + accumulatedValuesByDate[dateString].totalCurrentValue = + accumulatedValuesByDate[dateString].totalCurrentValue.add( + currentValue + ); + + accumulatedValuesByDate[ + dateString + ].totalCurrentValueWithCurrencyEffect = accumulatedValuesByDate[ + dateString + ].totalCurrentValueWithCurrencyEffect.add( + currentValueWithCurrencyEffect + ); + + accumulatedValuesByDate[dateString].totalInvestmentValue = + accumulatedValuesByDate[dateString].totalInvestmentValue.add( + investmentValueAccumulated + ); + + accumulatedValuesByDate[ + dateString + ].totalInvestmentValueWithCurrencyEffect = accumulatedValuesByDate[ + dateString + ].totalInvestmentValueWithCurrencyEffect.add( + investmentValueAccumulatedWithCurrencyEffect + ); + + accumulatedValuesByDate[dateString].totalNetPerformanceValue = + accumulatedValuesByDate[dateString].totalNetPerformanceValue.add( + netPerformanceValue + ); + + accumulatedValuesByDate[ + dateString + ].totalNetPerformanceValueWithCurrencyEffect = accumulatedValuesByDate[ + dateString + ].totalNetPerformanceValueWithCurrencyEffect.add( + netPerformanceValueWithCurrencyEffect + ); + + accumulatedValuesByDate[dateString].totalTimeWeightedInvestmentValue = + accumulatedValuesByDate[ + dateString + ].totalTimeWeightedInvestmentValue.add(timeWeightedInvestmentValue); + + accumulatedValuesByDate[ + dateString + ].totalTimeWeightedInvestmentValueWithCurrencyEffect = + accumulatedValuesByDate[ + dateString + ].totalTimeWeightedInvestmentValueWithCurrencyEffect.add( + timeWeightedInvestmentValueWithCurrencyEffect + ); + } + + if ( + this.accountBalanceItems.some(({ date }) => { + return date === dateString; + }) + ) { + accumulatedValuesByDate[ + dateString + ].totalAccountBalanceWithCurrencyEffect = new Big( + this.accountBalanceItems.find(({ date }) => { + return date === dateString; + }).value + ); + } else { + accumulatedValuesByDate[ + dateString + ].totalAccountBalanceWithCurrencyEffect = + accumulatedValuesByDate[lastDate] + ?.totalAccountBalanceWithCurrencyEffect ?? new Big(0); } + + lastDate = dateString; } return Object.entries(accumulatedValuesByDate).map(([date, values]) => { const { investmentValueWithCurrencyEffect, + totalAccountBalanceWithCurrencyEffect, totalCurrentValue, totalCurrentValueWithCurrencyEffect, totalInvestmentValue, @@ -661,6 +723,11 @@ export abstract class PortfolioCalculator { netPerformance: totalNetPerformanceValue.toNumber(), netPerformanceWithCurrencyEffect: totalNetPerformanceValueWithCurrencyEffect.toNumber(), + // TODO: Add valuables + netWorth: totalCurrentValueWithCurrencyEffect + .plus(totalAccountBalanceWithCurrencyEffect) + .toNumber(), + totalAccountBalance: totalAccountBalanceWithCurrencyEffect.toNumber(), totalInvestment: totalInvestmentValue.toNumber(), totalInvestmentValueWithCurrencyEffect: totalInvestmentValueWithCurrencyEffect.toNumber(), @@ -749,9 +816,30 @@ export abstract class PortfolioCalculator { } public getStartDate() { - return this.transactionPoints.length > 0 - ? parseDate(this.transactionPoints[0].date) - : new Date(); + let firstAccountBalanceDate: Date; + let firstActivityDate: Date; + + try { + const firstAccountBalanceDateString = first( + this.accountBalanceItems + )?.date; + firstAccountBalanceDate = firstAccountBalanceDateString + ? parseDate(firstAccountBalanceDateString) + : new Date(); + } catch (error) { + firstAccountBalanceDate = new Date(); + } + + try { + const firstActivityDateString = this.transactionPoints[0].date; + firstActivityDate = firstActivityDateString + ? parseDate(firstActivityDateString) + : new Date(); + } catch (error) { + firstActivityDate = new Date(); + } + + return min([firstAccountBalanceDate, firstActivityDate]); } protected abstract getSymbolMetrics({ diff --git a/apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-no-orders.spec.ts b/apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-no-orders.spec.ts index 1d69abfbf..bd04d6045 100644 --- a/apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-no-orders.spec.ts +++ b/apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-no-orders.spec.ts @@ -90,7 +90,12 @@ describe('PortfolioCalculator', () => { expect(investments).toEqual([]); - expect(investmentsByMonth).toEqual([]); + expect(investmentsByMonth).toEqual([ + { + date: '2021-12-01', + investment: 0 + } + ]); }); }); }); diff --git a/apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-novn-buy-and-sell.spec.ts b/apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-novn-buy-and-sell.spec.ts index 6f0b03800..0dd16b045 100644 --- a/apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-novn-buy-and-sell.spec.ts +++ b/apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-novn-buy-and-sell.spec.ts @@ -113,6 +113,8 @@ describe('PortfolioCalculator', () => { netPerformanceInPercentage: 0, netPerformanceInPercentageWithCurrencyEffect: 0, netPerformanceWithCurrencyEffect: 0, + netWorth: 151.6, + totalAccountBalance: 0, totalInvestment: 151.6, totalInvestmentValueWithCurrencyEffect: 151.6, value: 151.6, @@ -126,6 +128,8 @@ describe('PortfolioCalculator', () => { netPerformanceInPercentage: 13.100263852242744, netPerformanceInPercentageWithCurrencyEffect: 13.100263852242744, netPerformanceWithCurrencyEffect: 19.86, + netWorth: 0, + totalAccountBalance: 0, totalInvestment: 0, totalInvestmentValueWithCurrencyEffect: 0, value: 0, diff --git a/apps/api/src/app/portfolio/calculator/twr/portfolio-calculator.ts b/apps/api/src/app/portfolio/calculator/twr/portfolio-calculator.ts index 032d13a64..9458fb1bd 100644 --- a/apps/api/src/app/portfolio/calculator/twr/portfolio-calculator.ts +++ b/apps/api/src/app/portfolio/calculator/twr/portfolio-calculator.ts @@ -188,6 +188,7 @@ export class TWRPortfolioCalculator extends PortfolioCalculator { [date: string]: Big; } = {}; + let totalAccountBalanceInBaseCurrency = new Big(0); let totalDividend = new Big(0); let totalDividendInBaseCurrency = new Big(0); let totalInterest = new Big(0); @@ -237,6 +238,7 @@ export class TWRPortfolioCalculator extends PortfolioCalculator { timeWeightedInvestmentValues: {}, timeWeightedInvestmentValuesWithCurrencyEffect: {}, timeWeightedInvestmentWithCurrencyEffect: new Big(0), + totalAccountBalanceInBaseCurrency: new Big(0), totalDividend: new Big(0), totalDividendInBaseCurrency: new Big(0), totalInterest: new Big(0), @@ -286,6 +288,7 @@ export class TWRPortfolioCalculator extends PortfolioCalculator { timeWeightedInvestmentValues: {}, timeWeightedInvestmentValuesWithCurrencyEffect: {}, timeWeightedInvestmentWithCurrencyEffect: new Big(0), + totalAccountBalanceInBaseCurrency: new Big(0), totalDividend: new Big(0), totalDividendInBaseCurrency: new Big(0), totalInterest: new Big(0), @@ -875,6 +878,7 @@ export class TWRPortfolioCalculator extends PortfolioCalculator { netPerformanceValuesWithCurrencyEffect, timeWeightedInvestmentValues, timeWeightedInvestmentValuesWithCurrencyEffect, + totalAccountBalanceInBaseCurrency, totalDividend, totalDividendInBaseCurrency, totalInterest, diff --git a/apps/api/src/app/portfolio/portfolio.service.ts b/apps/api/src/app/portfolio/portfolio.service.ts index 4c65d2eb9..cf7a47f52 100644 --- a/apps/api/src/app/portfolio/portfolio.service.ts +++ b/apps/api/src/app/portfolio/portfolio.service.ts @@ -64,7 +64,6 @@ import { differenceInDays, format, isAfter, - isBefore, isSameMonth, isSameYear, parseISO, @@ -1056,11 +1055,16 @@ export class PortfolioService { ) => { const formattedDate = format(date, DATE_FORMAT); - // Store the item in the map, overwriting if the date already exists - map[formattedDate] = { - date: formattedDate, - value: valueInBaseCurrency - }; + if (map[formattedDate]) { + // If the value exists, add the current value to the existing one + map[formattedDate].value += valueInBaseCurrency; + } else { + // Otherwise, initialize the value for that date + map[formattedDate] = { + date: formattedDate, + value: valueInBaseCurrency + }; + } return map; }, @@ -1100,6 +1104,7 @@ export class PortfolioService { } const portfolioCalculator = this.calculatorFactory.createCalculator({ + accountBalanceItems, activities, dateRange, calculationType: PerformanceCalculationType.TWR, @@ -1131,6 +1136,8 @@ export class PortfolioService { let currentNetPerformanceWithCurrencyEffect = netPerformanceWithCurrencyEffect; + let currentNetWorth = 0; + const items = await portfolioCalculator.getChart({ dateRange }); @@ -1153,35 +1160,14 @@ export class PortfolioService { currentNetPerformanceWithCurrencyEffect = new Big( itemOfToday.netPerformanceWithCurrencyEffect ); - } - - accountBalanceItems = accountBalanceItems.filter(({ date }) => { - return !isBefore(parseDate(date), startDate); - }); - const accountBalanceItemOfToday = accountBalanceItems.find(({ date }) => { - return date === format(new Date(), DATE_FORMAT); - }); - - if (!accountBalanceItemOfToday) { - accountBalanceItems.push({ - date: format(new Date(), DATE_FORMAT), - value: last(accountBalanceItems)?.value ?? 0 - }); + currentNetWorth = itemOfToday.netWorth; } - const mergedHistoricalDataItems = this.mergeHistoricalDataItems( - accountBalanceItems, - items - ); - - const currentHistoricalDataItem = last(mergedHistoricalDataItems); - const currentNetWorth = currentHistoricalDataItem?.netWorth ?? 0; - return { errors, hasErrors, - chart: mergedHistoricalDataItems, + chart: items, firstOrderDate: parseDate(items[0]?.date), performance: { currentNetWorth, @@ -1909,44 +1895,4 @@ export class PortfolioService { return { accounts, platforms }; } - - private mergeHistoricalDataItems( - accountBalanceItems: HistoricalDataItem[], - performanceChartItems: HistoricalDataItem[] - ): HistoricalDataItem[] { - const historicalDataItemsMap: { [date: string]: HistoricalDataItem } = {}; - let latestAccountBalance = 0; - - for (const item of accountBalanceItems.concat(performanceChartItems)) { - const isAccountBalanceItem = accountBalanceItems.includes(item); - - const totalAccountBalance = isAccountBalanceItem - ? item.value - : latestAccountBalance; - - if (isAccountBalanceItem && performanceChartItems.length > 0) { - latestAccountBalance = item.value; - } else { - historicalDataItemsMap[item.date] = { - ...item, - totalAccountBalance, - netWorth: - (isAccountBalanceItem ? 0 : item.value) + totalAccountBalance - }; - } - } - - // Convert to an array and sort by date in ascending order - const historicalDataItems = Object.keys(historicalDataItemsMap).map( - (date) => { - return historicalDataItemsMap[date]; - } - ); - - historicalDataItems.sort((a, b) => { - return new Date(a.date).getTime() - new Date(b.date).getTime(); - }); - - return historicalDataItems; - } } diff --git a/apps/client/src/app/components/account-detail-dialog/account-detail-dialog.component.ts b/apps/client/src/app/components/account-detail-dialog/account-detail-dialog.component.ts index 2c2036b16..aeec7d2a5 100644 --- a/apps/client/src/app/components/account-detail-dialog/account-detail-dialog.component.ts +++ b/apps/client/src/app/components/account-detail-dialog/account-detail-dialog.component.ts @@ -84,55 +84,10 @@ export class AccountDetailDialog implements OnDestroy, OnInit { } public ngOnInit() { - this.dataService - .fetchAccount(this.data.accountId) - .pipe(takeUntil(this.unsubscribeSubject)) - .subscribe( - ({ - balance, - currency, - name, - Platform, - transactionCount, - value, - valueInBaseCurrency - }) => { - this.balance = balance; - this.currency = currency; - - if (isNumber(balance) && isNumber(value)) { - this.equity = new Big(value).minus(balance).toNumber(); - } else { - this.equity = null; - } - - this.name = name; - this.platformName = Platform?.name ?? '-'; - this.transactionCount = transactionCount; - this.valueInBaseCurrency = valueInBaseCurrency; - - this.changeDetectorRef.markForCheck(); - } - ); - - this.dataService - .fetchPortfolioHoldings({ - filters: [ - { - type: 'ACCOUNT', - id: this.data.accountId - } - ] - }) - .pipe(takeUntil(this.unsubscribeSubject)) - .subscribe(({ holdings }) => { - this.holdings = holdings; - - this.changeDetectorRef.markForCheck(); - }); - + this.fetchAccount(); this.fetchAccountBalances(); this.fetchActivities(); + this.fetchPortfolioHoldings(); this.fetchPortfolioPerformance(); } @@ -155,6 +110,7 @@ export class AccountDetailDialog implements OnDestroy, OnInit { }) .pipe(takeUntil(this.unsubscribeSubject)) .subscribe(() => { + this.fetchAccount(); this.fetchAccountBalances(); this.fetchPortfolioPerformance(); }); @@ -165,6 +121,7 @@ export class AccountDetailDialog implements OnDestroy, OnInit { .deleteAccountBalance(aId) .pipe(takeUntil(this.unsubscribeSubject)) .subscribe(() => { + this.fetchAccount(); this.fetchAccountBalances(); this.fetchPortfolioPerformance(); }); @@ -199,6 +156,39 @@ export class AccountDetailDialog implements OnDestroy, OnInit { this.fetchActivities(); } + private fetchAccount() { + this.dataService + .fetchAccount(this.data.accountId) + .pipe(takeUntil(this.unsubscribeSubject)) + .subscribe( + ({ + balance, + currency, + name, + Platform, + transactionCount, + value, + valueInBaseCurrency + }) => { + this.balance = balance; + this.currency = currency; + + if (isNumber(balance) && isNumber(value)) { + this.equity = new Big(value).minus(balance).toNumber(); + } else { + this.equity = null; + } + + this.name = name; + this.platformName = Platform?.name ?? '-'; + this.transactionCount = transactionCount; + this.valueInBaseCurrency = valueInBaseCurrency; + + this.changeDetectorRef.markForCheck(); + } + ); + } + private fetchAccountBalances() { this.dataService .fetchAccountBalances(this.data.accountId) @@ -230,6 +220,24 @@ export class AccountDetailDialog implements OnDestroy, OnInit { }); } + private fetchPortfolioHoldings() { + this.dataService + .fetchPortfolioHoldings({ + filters: [ + { + type: 'ACCOUNT', + id: this.data.accountId + } + ] + }) + .pipe(takeUntil(this.unsubscribeSubject)) + .subscribe(({ holdings }) => { + this.holdings = holdings; + + this.changeDetectorRef.markForCheck(); + }); + } + private fetchPortfolioPerformance() { this.isLoadingChart = true; @@ -251,11 +259,7 @@ export class AccountDetailDialog implements OnDestroy, OnInit { ({ date, netWorth, netWorthInPercentage }) => { return { date, - value: - this.data.hasImpersonationId || - this.user.settings.isRestrictedView - ? netWorthInPercentage - : netWorth + value: isNumber(netWorth) ? netWorth : netWorthInPercentage }; } ); diff --git a/apps/client/src/app/pages/accounts/accounts-page.component.ts b/apps/client/src/app/pages/accounts/accounts-page.component.ts index 93d88cd64..e1b53acd3 100644 --- a/apps/client/src/app/pages/accounts/accounts-page.component.ts +++ b/apps/client/src/app/pages/accounts/accounts-page.component.ts @@ -233,6 +233,8 @@ export class AccountsPageComponent implements OnDestroy, OnInit { .afterClosed() .pipe(takeUntil(this.unsubscribeSubject)) .subscribe(() => { + this.fetchAccounts(); + this.router.navigate(['.'], { relativeTo: this.route }); }); } diff --git a/libs/common/src/lib/interfaces/symbol-metrics.interface.ts b/libs/common/src/lib/interfaces/symbol-metrics.interface.ts index 57eed9212..6b78f273a 100644 --- a/libs/common/src/lib/interfaces/symbol-metrics.interface.ts +++ b/libs/common/src/lib/interfaces/symbol-metrics.interface.ts @@ -40,6 +40,7 @@ export interface SymbolMetrics { [date: string]: Big; }; timeWeightedInvestmentWithCurrencyEffect: Big; + totalAccountBalanceInBaseCurrency: Big; totalDividend: Big; totalDividendInBaseCurrency: Big; totalInterest: Big;