Feature/improve experimental chart in account detail dialog (#3813)
* Improve chart in account detail dialog * Update changelog
This commit is contained in:
parent
4a97e2bb54
commit
bb445ddf2e
@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- Improved the usability of various action menus by introducing horizontal lines to separate the delete action
|
- Improved the usability of various action menus by introducing horizontal lines to separate the delete action
|
||||||
|
- Improved the chart in the account detail dialog (experimental)
|
||||||
- Aligned the holdings and regions of the public page with the allocations page
|
- Aligned the holdings and regions of the public page with the allocations page
|
||||||
- Considered the user’s language in the link of the access table to share the portfolio
|
- Considered the user’s language in the link of the access table to share the portfolio
|
||||||
- Improved the language localization for German (`de`)
|
- Improved the language localization for German (`de`)
|
||||||
|
@ -2,14 +2,18 @@ import { PortfolioChangedEvent } from '@ghostfolio/api/events/portfolio-changed.
|
|||||||
import { LogPerformance } from '@ghostfolio/api/interceptors/performance-logging/performance-logging.interceptor';
|
import { LogPerformance } from '@ghostfolio/api/interceptors/performance-logging/performance-logging.interceptor';
|
||||||
import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service';
|
import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service';
|
||||||
import { PrismaService } from '@ghostfolio/api/services/prisma/prisma.service';
|
import { PrismaService } from '@ghostfolio/api/services/prisma/prisma.service';
|
||||||
import { resetHours } from '@ghostfolio/common/helper';
|
import { DATE_FORMAT, getSum, resetHours } from '@ghostfolio/common/helper';
|
||||||
import { AccountBalancesResponse, Filter } from '@ghostfolio/common/interfaces';
|
import {
|
||||||
import { UserWithSettings } from '@ghostfolio/common/types';
|
AccountBalancesResponse,
|
||||||
|
Filter,
|
||||||
|
HistoricalDataItem
|
||||||
|
} from '@ghostfolio/common/interfaces';
|
||||||
|
|
||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
import { EventEmitter2 } from '@nestjs/event-emitter';
|
import { EventEmitter2 } from '@nestjs/event-emitter';
|
||||||
import { AccountBalance, Prisma } from '@prisma/client';
|
import { AccountBalance, Prisma } from '@prisma/client';
|
||||||
import { parseISO } from 'date-fns';
|
import { Big } from 'big.js';
|
||||||
|
import { format, parseISO } from 'date-fns';
|
||||||
|
|
||||||
import { CreateAccountBalanceDto } from './create-account-balance.dto';
|
import { CreateAccountBalanceDto } from './create-account-balance.dto';
|
||||||
|
|
||||||
@ -91,17 +95,55 @@ export class AccountBalanceService {
|
|||||||
return accountBalance;
|
return accountBalance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async getAccountBalanceItems({
|
||||||
|
filters,
|
||||||
|
userCurrency,
|
||||||
|
userId
|
||||||
|
}: {
|
||||||
|
filters?: Filter[];
|
||||||
|
userCurrency: string;
|
||||||
|
userId: string;
|
||||||
|
}): Promise<HistoricalDataItem[]> {
|
||||||
|
const { balances } = await this.getAccountBalances({
|
||||||
|
filters,
|
||||||
|
userCurrency,
|
||||||
|
userId,
|
||||||
|
withExcludedAccounts: false // TODO
|
||||||
|
});
|
||||||
|
const accumulatedBalancesByDate: { [date: string]: HistoricalDataItem } =
|
||||||
|
{};
|
||||||
|
const lastBalancesByAccount: { [accountId: string]: Big } = {};
|
||||||
|
|
||||||
|
for (const { accountId, date, valueInBaseCurrency } of balances) {
|
||||||
|
const formattedDate = format(date, DATE_FORMAT);
|
||||||
|
|
||||||
|
lastBalancesByAccount[accountId] = new Big(valueInBaseCurrency);
|
||||||
|
|
||||||
|
const totalBalance = getSum(Object.values(lastBalancesByAccount));
|
||||||
|
|
||||||
|
// Add or update the accumulated balance for this date
|
||||||
|
accumulatedBalancesByDate[formattedDate] = {
|
||||||
|
date: formattedDate,
|
||||||
|
value: totalBalance.toNumber()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return Object.values(accumulatedBalancesByDate);
|
||||||
|
}
|
||||||
|
|
||||||
@LogPerformance
|
@LogPerformance
|
||||||
public async getAccountBalances({
|
public async getAccountBalances({
|
||||||
filters,
|
filters,
|
||||||
user,
|
userCurrency,
|
||||||
|
userId,
|
||||||
withExcludedAccounts
|
withExcludedAccounts
|
||||||
}: {
|
}: {
|
||||||
filters?: Filter[];
|
filters?: Filter[];
|
||||||
user: UserWithSettings;
|
userCurrency: string;
|
||||||
|
userId: string;
|
||||||
withExcludedAccounts?: boolean;
|
withExcludedAccounts?: boolean;
|
||||||
}): Promise<AccountBalancesResponse> {
|
}): Promise<AccountBalancesResponse> {
|
||||||
const where: Prisma.AccountBalanceWhereInput = { userId: user.id };
|
const where: Prisma.AccountBalanceWhereInput = { userId };
|
||||||
|
|
||||||
const accountFilter = filters?.find(({ type }) => {
|
const accountFilter = filters?.find(({ type }) => {
|
||||||
return type === 'ACCOUNT';
|
return type === 'ACCOUNT';
|
||||||
@ -132,10 +174,11 @@ export class AccountBalanceService {
|
|||||||
balances: balances.map((balance) => {
|
balances: balances.map((balance) => {
|
||||||
return {
|
return {
|
||||||
...balance,
|
...balance,
|
||||||
|
accountId: balance.Account.id,
|
||||||
valueInBaseCurrency: this.exchangeRateDataService.toCurrency(
|
valueInBaseCurrency: this.exchangeRateDataService.toCurrency(
|
||||||
balance.value,
|
balance.value,
|
||||||
balance.Account.currency,
|
balance.Account.currency,
|
||||||
user.Settings.settings.baseCurrency
|
userCurrency
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
|
@ -137,7 +137,8 @@ export class AccountController {
|
|||||||
): Promise<AccountBalancesResponse> {
|
): Promise<AccountBalancesResponse> {
|
||||||
return this.accountBalanceService.getAccountBalances({
|
return this.accountBalanceService.getAccountBalances({
|
||||||
filters: [{ id, type: 'ACCOUNT' }],
|
filters: [{ id, type: 'ACCOUNT' }],
|
||||||
user: this.request.user
|
userCurrency: this.request.user.Settings.settings.baseCurrency,
|
||||||
|
userId: this.request.user.id
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -104,6 +104,10 @@ export abstract class PortfolioCalculator {
|
|||||||
|
|
||||||
let dateOfFirstActivity = new Date();
|
let dateOfFirstActivity = new Date();
|
||||||
|
|
||||||
|
if (this.accountBalanceItems[0]) {
|
||||||
|
dateOfFirstActivity = parseDate(this.accountBalanceItems[0].date);
|
||||||
|
}
|
||||||
|
|
||||||
this.activities = activities
|
this.activities = activities
|
||||||
.map(
|
.map(
|
||||||
({
|
({
|
||||||
@ -269,6 +273,10 @@ export abstract class PortfolioCalculator {
|
|||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
for (const accountBalanceItem of this.accountBalanceItems) {
|
||||||
|
chartDateMap[accountBalanceItem.date] = true;
|
||||||
|
}
|
||||||
|
|
||||||
const chartDates = sortBy(Object.keys(chartDateMap), (chartDate) => {
|
const chartDates = sortBy(Object.keys(chartDateMap), (chartDate) => {
|
||||||
return chartDate;
|
return chartDate;
|
||||||
});
|
});
|
||||||
@ -447,9 +455,28 @@ export abstract class PortfolioCalculator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let lastDate = chartDates[0];
|
const accountBalanceItemsMap = this.accountBalanceItems.reduce(
|
||||||
|
(map, { date, value }) => {
|
||||||
|
map[date] = new Big(value);
|
||||||
|
|
||||||
|
return map;
|
||||||
|
},
|
||||||
|
{} as { [date: string]: Big }
|
||||||
|
);
|
||||||
|
|
||||||
|
const accountBalanceMap: { [date: string]: Big } = {};
|
||||||
|
|
||||||
|
let lastKnownBalance = new Big(0);
|
||||||
|
|
||||||
for (const dateString of chartDates) {
|
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;
|
||||||
|
|
||||||
for (const symbol of Object.keys(valuesBySymbol)) {
|
for (const symbol of Object.keys(valuesBySymbol)) {
|
||||||
const symbolValues = valuesBySymbol[symbol];
|
const symbolValues = valuesBySymbol[symbol];
|
||||||
|
|
||||||
@ -492,18 +519,7 @@ export abstract class PortfolioCalculator {
|
|||||||
accumulatedValuesByDate[dateString]
|
accumulatedValuesByDate[dateString]
|
||||||
?.investmentValueWithCurrencyEffect ?? new Big(0)
|
?.investmentValueWithCurrencyEffect ?? new Big(0)
|
||||||
).add(investmentValueWithCurrencyEffect),
|
).add(investmentValueWithCurrencyEffect),
|
||||||
totalAccountBalanceWithCurrencyEffect: this.accountBalanceItems.some(
|
totalAccountBalanceWithCurrencyEffect: accountBalanceMap[dateString],
|
||||||
({ date }) => {
|
|
||||||
return date === dateString;
|
|
||||||
}
|
|
||||||
)
|
|
||||||
? new Big(
|
|
||||||
this.accountBalanceItems.find(({ date }) => {
|
|
||||||
return date === dateString;
|
|
||||||
}).value
|
|
||||||
)
|
|
||||||
: (accumulatedValuesByDate[lastDate]
|
|
||||||
?.totalAccountBalanceWithCurrencyEffect ?? new Big(0)),
|
|
||||||
totalCurrentValue: (
|
totalCurrentValue: (
|
||||||
accumulatedValuesByDate[dateString]?.totalCurrentValue ?? new Big(0)
|
accumulatedValuesByDate[dateString]?.totalCurrentValue ?? new Big(0)
|
||||||
).add(currentValue),
|
).add(currentValue),
|
||||||
@ -537,8 +553,6 @@ export abstract class PortfolioCalculator {
|
|||||||
).add(timeWeightedInvestmentValueWithCurrencyEffect)
|
).add(timeWeightedInvestmentValueWithCurrencyEffect)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
lastDate = dateString;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const historicalData: HistoricalDataItem[] = Object.entries(
|
const historicalData: HistoricalDataItem[] = Object.entries(
|
||||||
@ -733,12 +747,12 @@ export abstract class PortfolioCalculator {
|
|||||||
timeWeightedInvestmentValue === 0
|
timeWeightedInvestmentValue === 0
|
||||||
? 0
|
? 0
|
||||||
: netPerformanceWithCurrencyEffectSinceStartDate /
|
: netPerformanceWithCurrencyEffectSinceStartDate /
|
||||||
timeWeightedInvestmentValue,
|
timeWeightedInvestmentValue
|
||||||
// TODO: Add net worth with valuables
|
// TODO: Add net worth with valuables
|
||||||
// netWorth: totalCurrentValueWithCurrencyEffect
|
// netWorth: totalCurrentValueWithCurrencyEffect
|
||||||
// .plus(totalAccountBalanceWithCurrencyEffect)
|
// .plus(totalAccountBalanceWithCurrencyEffect)
|
||||||
// .toNumber()
|
// .toNumber()
|
||||||
netWorth: 0
|
// netWorth: 0
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -815,7 +829,7 @@ export abstract class PortfolioCalculator {
|
|||||||
endDate: Date;
|
endDate: Date;
|
||||||
startDate: Date;
|
startDate: Date;
|
||||||
step: number;
|
step: number;
|
||||||
}) {
|
}): { [date: string]: true } {
|
||||||
// Create a map of all relevant chart dates:
|
// Create a map of all relevant chart dates:
|
||||||
// 1. Add transaction point dates
|
// 1. Add transaction point dates
|
||||||
let chartDateMap = this.transactionPoints.reduce((result, { date }) => {
|
let chartDateMap = this.transactionPoints.reduce((result, { date }) => {
|
||||||
|
@ -1058,35 +1058,12 @@ export class PortfolioService {
|
|||||||
const user = await this.userService.user({ id: userId });
|
const user = await this.userService.user({ id: userId });
|
||||||
const userCurrency = this.getUserCurrency(user);
|
const userCurrency = this.getUserCurrency(user);
|
||||||
|
|
||||||
const accountBalances = await this.accountBalanceService.getAccountBalances(
|
const accountBalanceItems =
|
||||||
{ filters, user, withExcludedAccounts }
|
await this.accountBalanceService.getAccountBalanceItems({
|
||||||
);
|
filters,
|
||||||
|
userId,
|
||||||
let accountBalanceItems: HistoricalDataItem[] = Object.values(
|
userCurrency
|
||||||
// Reduce the array to a map with unique dates as keys
|
});
|
||||||
accountBalances.balances.reduce(
|
|
||||||
(
|
|
||||||
map: { [date: string]: HistoricalDataItem },
|
|
||||||
{ date, valueInBaseCurrency }
|
|
||||||
) => {
|
|
||||||
const formattedDate = format(date, DATE_FORMAT);
|
|
||||||
|
|
||||||
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;
|
|
||||||
},
|
|
||||||
{}
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
const { activities } =
|
const { activities } =
|
||||||
await this.orderService.getOrdersForPortfolioCalculator({
|
await this.orderService.getOrdersForPortfolioCalculator({
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { AccountBalanceModule } from '@ghostfolio/api/app/account-balance/account-balance.module';
|
||||||
import { OrderModule } from '@ghostfolio/api/app/order/order.module';
|
import { OrderModule } from '@ghostfolio/api/app/order/order.module';
|
||||||
import { PortfolioCalculatorFactory } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator.factory';
|
import { PortfolioCalculatorFactory } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator.factory';
|
||||||
import { CurrentRateService } from '@ghostfolio/api/app/portfolio/current-rate.service';
|
import { CurrentRateService } from '@ghostfolio/api/app/portfolio/current-rate.service';
|
||||||
@ -17,6 +18,7 @@ import { PortfolioSnapshotProcessor } from './portfolio-snapshot.processor';
|
|||||||
@Module({
|
@Module({
|
||||||
exports: [BullModule, PortfolioSnapshotService],
|
exports: [BullModule, PortfolioSnapshotService],
|
||||||
imports: [
|
imports: [
|
||||||
|
AccountBalanceModule,
|
||||||
BullModule.registerQueue({
|
BullModule.registerQueue({
|
||||||
name: PORTFOLIO_SNAPSHOT_QUEUE
|
name: PORTFOLIO_SNAPSHOT_QUEUE
|
||||||
}),
|
}),
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { AccountBalanceService } from '@ghostfolio/api/app/account-balance/account-balance.service';
|
||||||
import { OrderService } from '@ghostfolio/api/app/order/order.service';
|
import { OrderService } from '@ghostfolio/api/app/order/order.service';
|
||||||
import {
|
import {
|
||||||
PerformanceCalculationType,
|
PerformanceCalculationType,
|
||||||
@ -24,6 +25,7 @@ import { IPortfolioSnapshotQueueJob } from './interfaces/portfolio-snapshot-queu
|
|||||||
@Processor(PORTFOLIO_SNAPSHOT_QUEUE)
|
@Processor(PORTFOLIO_SNAPSHOT_QUEUE)
|
||||||
export class PortfolioSnapshotProcessor {
|
export class PortfolioSnapshotProcessor {
|
||||||
public constructor(
|
public constructor(
|
||||||
|
private readonly accountBalanceService: AccountBalanceService,
|
||||||
private readonly calculatorFactory: PortfolioCalculatorFactory,
|
private readonly calculatorFactory: PortfolioCalculatorFactory,
|
||||||
private readonly configurationService: ConfigurationService,
|
private readonly configurationService: ConfigurationService,
|
||||||
private readonly orderService: OrderService,
|
private readonly orderService: OrderService,
|
||||||
@ -56,7 +58,15 @@ export class PortfolioSnapshotProcessor {
|
|||||||
userId: job.data.userId
|
userId: job.data.userId
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const accountBalanceItems =
|
||||||
|
await this.accountBalanceService.getAccountBalanceItems({
|
||||||
|
filters: job.data.filters,
|
||||||
|
userCurrency: job.data.userCurrency,
|
||||||
|
userId: job.data.userId
|
||||||
|
});
|
||||||
|
|
||||||
const portfolioCalculator = this.calculatorFactory.createCalculator({
|
const portfolioCalculator = this.calculatorFactory.createCalculator({
|
||||||
|
accountBalanceItems,
|
||||||
activities,
|
activities,
|
||||||
calculationType: PerformanceCalculationType.TWR,
|
calculationType: PerformanceCalculationType.TWR,
|
||||||
currency: job.data.userCurrency,
|
currency: job.data.userCurrency,
|
||||||
|
@ -2,7 +2,7 @@ import { CreateAccountBalanceDto } from '@ghostfolio/api/app/account-balance/cre
|
|||||||
import { Activity } from '@ghostfolio/api/app/order/interfaces/activities.interface';
|
import { Activity } from '@ghostfolio/api/app/order/interfaces/activities.interface';
|
||||||
import { DataService } from '@ghostfolio/client/services/data.service';
|
import { DataService } from '@ghostfolio/client/services/data.service';
|
||||||
import { UserService } from '@ghostfolio/client/services/user/user.service';
|
import { UserService } from '@ghostfolio/client/services/user/user.service';
|
||||||
import { downloadAsFile } from '@ghostfolio/common/helper';
|
import { DATE_FORMAT, downloadAsFile } from '@ghostfolio/common/helper';
|
||||||
import {
|
import {
|
||||||
AccountBalancesResponse,
|
AccountBalancesResponse,
|
||||||
HistoricalDataItem,
|
HistoricalDataItem,
|
||||||
@ -27,7 +27,7 @@ import { Router } from '@angular/router';
|
|||||||
import { Big } from 'big.js';
|
import { Big } from 'big.js';
|
||||||
import { format, parseISO } from 'date-fns';
|
import { format, parseISO } from 'date-fns';
|
||||||
import { isNumber } from 'lodash';
|
import { isNumber } from 'lodash';
|
||||||
import { Subject } from 'rxjs';
|
import { forkJoin, Subject } from 'rxjs';
|
||||||
import { takeUntil } from 'rxjs/operators';
|
import { takeUntil } from 'rxjs/operators';
|
||||||
|
|
||||||
import { AccountDetailDialogParams } from './interfaces/interfaces';
|
import { AccountDetailDialogParams } from './interfaces/interfaces';
|
||||||
@ -87,11 +87,7 @@ export class AccountDetailDialog implements OnDestroy, OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public ngOnInit() {
|
public ngOnInit() {
|
||||||
this.fetchAccount();
|
this.initialize();
|
||||||
this.fetchAccountBalances();
|
|
||||||
this.fetchActivities();
|
|
||||||
this.fetchPortfolioHoldings();
|
|
||||||
this.fetchPortfolioPerformance();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public onCloneActivity(aActivity: Activity) {
|
public onCloneActivity(aActivity: Activity) {
|
||||||
@ -111,9 +107,7 @@ export class AccountDetailDialog implements OnDestroy, OnInit {
|
|||||||
.postAccountBalance(accountBalance)
|
.postAccountBalance(accountBalance)
|
||||||
.pipe(takeUntil(this.unsubscribeSubject))
|
.pipe(takeUntil(this.unsubscribeSubject))
|
||||||
.subscribe(() => {
|
.subscribe(() => {
|
||||||
this.fetchAccount();
|
this.initialize();
|
||||||
this.fetchAccountBalances();
|
|
||||||
this.fetchPortfolioPerformance();
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -122,9 +116,7 @@ export class AccountDetailDialog implements OnDestroy, OnInit {
|
|||||||
.deleteAccountBalance(aId)
|
.deleteAccountBalance(aId)
|
||||||
.pipe(takeUntil(this.unsubscribeSubject))
|
.pipe(takeUntil(this.unsubscribeSubject))
|
||||||
.subscribe(() => {
|
.subscribe(() => {
|
||||||
this.fetchAccount();
|
this.initialize();
|
||||||
this.fetchAccountBalances();
|
|
||||||
this.fetchPortfolioPerformance();
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -198,17 +190,6 @@ export class AccountDetailDialog implements OnDestroy, OnInit {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private fetchAccountBalances() {
|
|
||||||
this.dataService
|
|
||||||
.fetchAccountBalances(this.data.accountId)
|
|
||||||
.pipe(takeUntil(this.unsubscribeSubject))
|
|
||||||
.subscribe(({ balances }) => {
|
|
||||||
this.accountBalances = balances;
|
|
||||||
|
|
||||||
this.changeDetectorRef.markForCheck();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private fetchActivities() {
|
private fetchActivities() {
|
||||||
this.isLoadingActivities = true;
|
this.isLoadingActivities = true;
|
||||||
|
|
||||||
@ -229,6 +210,58 @@ export class AccountDetailDialog implements OnDestroy, OnInit {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fetchChart() {
|
||||||
|
this.isLoadingChart = true;
|
||||||
|
|
||||||
|
forkJoin({
|
||||||
|
accountBalances: this.dataService
|
||||||
|
.fetchAccountBalances(this.data.accountId)
|
||||||
|
.pipe(takeUntil(this.unsubscribeSubject)),
|
||||||
|
portfolioPerformance: this.dataService
|
||||||
|
.fetchPortfolioPerformance({
|
||||||
|
filters: [
|
||||||
|
{
|
||||||
|
id: this.data.accountId,
|
||||||
|
type: 'ACCOUNT'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
range: 'max',
|
||||||
|
withExcludedAccounts: true,
|
||||||
|
withItems: true
|
||||||
|
})
|
||||||
|
.pipe(takeUntil(this.unsubscribeSubject))
|
||||||
|
}).subscribe({
|
||||||
|
error: () => {
|
||||||
|
this.isLoadingChart = false;
|
||||||
|
},
|
||||||
|
next: ({ accountBalances, portfolioPerformance }) => {
|
||||||
|
this.accountBalances = accountBalances.balances;
|
||||||
|
|
||||||
|
if (portfolioPerformance.chart.length > 0) {
|
||||||
|
this.historicalDataItems = portfolioPerformance.chart.map(
|
||||||
|
({ date, netWorth, netWorthInPercentage }) => ({
|
||||||
|
date,
|
||||||
|
value: isNumber(netWorth) ? netWorth : netWorthInPercentage
|
||||||
|
})
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
this.historicalDataItems = this.accountBalances.map(
|
||||||
|
({ date, valueInBaseCurrency }) => {
|
||||||
|
return {
|
||||||
|
date: format(date, DATE_FORMAT),
|
||||||
|
value: valueInBaseCurrency
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.isLoadingChart = false;
|
||||||
|
|
||||||
|
this.changeDetectorRef.markForCheck();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private fetchPortfolioHoldings() {
|
private fetchPortfolioHoldings() {
|
||||||
this.dataService
|
this.dataService
|
||||||
.fetchPortfolioHoldings({
|
.fetchPortfolioHoldings({
|
||||||
@ -247,36 +280,11 @@ export class AccountDetailDialog implements OnDestroy, OnInit {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private fetchPortfolioPerformance() {
|
private initialize() {
|
||||||
this.isLoadingChart = true;
|
this.fetchAccount();
|
||||||
|
this.fetchActivities();
|
||||||
this.dataService
|
this.fetchChart();
|
||||||
.fetchPortfolioPerformance({
|
this.fetchPortfolioHoldings();
|
||||||
filters: [
|
|
||||||
{
|
|
||||||
id: this.data.accountId,
|
|
||||||
type: 'ACCOUNT'
|
|
||||||
}
|
|
||||||
],
|
|
||||||
range: 'max',
|
|
||||||
withExcludedAccounts: true,
|
|
||||||
withItems: true
|
|
||||||
})
|
|
||||||
.pipe(takeUntil(this.unsubscribeSubject))
|
|
||||||
.subscribe(({ chart }) => {
|
|
||||||
this.historicalDataItems = chart.map(
|
|
||||||
({ date, netWorth, netWorthInPercentage }) => {
|
|
||||||
return {
|
|
||||||
date,
|
|
||||||
value: isNumber(netWorth) ? netWorth : netWorthInPercentage
|
|
||||||
};
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
this.isLoadingChart = false;
|
|
||||||
|
|
||||||
this.changeDetectorRef.markForCheck();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ngOnDestroy() {
|
public ngOnDestroy() {
|
||||||
|
@ -20,20 +20,20 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- TODO
|
@if (user?.settings?.isExperimentalFeatures) {
|
||||||
<div class="chart-container mb-3">
|
<div class="chart-container mb-3">
|
||||||
<gf-investment-chart
|
<gf-investment-chart
|
||||||
class="h-100"
|
class="h-100"
|
||||||
[currency]="user?.settings?.baseCurrency"
|
[currency]="user?.settings?.baseCurrency"
|
||||||
[historicalDataItems]="historicalDataItems"
|
[historicalDataItems]="historicalDataItems"
|
||||||
[isInPercent]="
|
[isInPercent]="
|
||||||
data.hasImpersonationId || user.settings.isRestrictedView
|
data.hasImpersonationId || user.settings.isRestrictedView
|
||||||
"
|
"
|
||||||
[isLoading]="isLoadingChart"
|
[isLoading]="isLoadingChart"
|
||||||
[locale]="user?.settings?.locale"
|
[locale]="user?.settings?.locale"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
-->
|
}
|
||||||
|
|
||||||
<div class="mb-3 row">
|
<div class="mb-3 row">
|
||||||
<div class="col-6 mb-3">
|
<div class="col-6 mb-3">
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { AccountBalance } from '@prisma/client';
|
import { AccountBalance } from '@prisma/client';
|
||||||
|
|
||||||
export interface AccountBalancesResponse {
|
export interface AccountBalancesResponse {
|
||||||
balances: (Pick<AccountBalance, 'date' | 'id' | 'value'> & {
|
balances: (Pick<AccountBalance, 'accountId' | 'date' | 'id' | 'value'> & {
|
||||||
valueInBaseCurrency: number;
|
valueInBaseCurrency: number;
|
||||||
})[];
|
})[];
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user