Feature/improve chart in account detail dialog (#3314)
* Improve net worth calculation in portfolio performance chart * Improve account balance management * Update changelog
This commit is contained in:
parent
ab59eb5c92
commit
39bd4a349b
@ -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
|
||||
|
@ -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<Account> {
|
||||
const { data, where } = params;
|
||||
|
||||
await this.prismaService.accountBalance.create({
|
||||
data: {
|
||||
Account: {
|
||||
connect: {
|
||||
id_userId: where.id_userId
|
||||
}
|
||||
},
|
||||
value: <number>data.balance
|
||||
}
|
||||
await this.accountBalanceService.createOrUpdateAccountBalance({
|
||||
accountId: <string>data.id,
|
||||
balance: <number>data.balance,
|
||||
date: format(new Date(), DATE_FORMAT),
|
||||
userId: aUserId
|
||||
});
|
||||
|
||||
return this.prismaService.account.update({
|
||||
|
@ -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,
|
||||
|
@ -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<HistoricalDataItem[]> {
|
||||
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({
|
||||
|
@ -90,7 +90,12 @@ describe('PortfolioCalculator', () => {
|
||||
|
||||
expect(investments).toEqual([]);
|
||||
|
||||
expect(investmentsByMonth).toEqual([]);
|
||||
expect(investmentsByMonth).toEqual([
|
||||
{
|
||||
date: '2021-12-01',
|
||||
investment: 0
|
||||
}
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
);
|
||||
|
||||
currentNetWorth = itemOfToday.netWorth;
|
||||
}
|
||||
|
||||
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
|
||||
});
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
};
|
||||
}
|
||||
);
|
||||
|
@ -233,6 +233,8 @@ export class AccountsPageComponent implements OnDestroy, OnInit {
|
||||
.afterClosed()
|
||||
.pipe(takeUntil(this.unsubscribeSubject))
|
||||
.subscribe(() => {
|
||||
this.fetchAccounts();
|
||||
|
||||
this.router.navigate(['.'], { relativeTo: this.route });
|
||||
});
|
||||
}
|
||||
|
@ -40,6 +40,7 @@ export interface SymbolMetrics {
|
||||
[date: string]: Big;
|
||||
};
|
||||
timeWeightedInvestmentWithCurrencyEffect: Big;
|
||||
totalAccountBalanceInBaseCurrency: Big;
|
||||
totalDividend: Big;
|
||||
totalDividendInBaseCurrency: Big;
|
||||
totalInterest: Big;
|
||||
|
Loading…
x
Reference in New Issue
Block a user