Browse Source

Integrate chart calculation into snapshot calculation

pull/3383/head
Reto Kaul 1 year ago
committed by Thomas Kaul
parent
commit
c5ec3c9b6c
  1. 3
      apps/api/src/app/order/order.service.ts
  2. 233
      apps/api/src/app/portfolio/calculator/portfolio-calculator.ts
  3. 6
      apps/api/src/app/portfolio/calculator/twr/portfolio-calculator.ts
  4. 12
      apps/api/src/app/portfolio/portfolio.controller.ts
  5. 93
      apps/api/src/app/portfolio/portfolio.service.ts
  6. 5
      libs/common/src/lib/models/portfolio-snapshot.ts

3
apps/api/src/app/order/order.service.ts

@ -247,6 +247,7 @@ export class OrderService {
userId: string; userId: string;
withExcludedAccounts?: boolean; withExcludedAccounts?: boolean;
}): Promise<Activities> { }): Promise<Activities> {
console.time('------ OrderService.getOrders');
let orderBy: Prisma.Enumerable<Prisma.OrderOrderByWithRelationInput> = [ let orderBy: Prisma.Enumerable<Prisma.OrderOrderByWithRelationInput> = [
{ date: 'asc' } { date: 'asc' }
]; ];
@ -406,6 +407,8 @@ export class OrderService {
}; };
}); });
console.timeEnd('------ OrderService.getOrders');
return { activities, count }; return { activities, count };
} }

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

@ -90,6 +90,7 @@ export abstract class PortfolioCalculator {
useCache: boolean; useCache: boolean;
userId: string; userId: string;
}) { }) {
console.time('--- PortfolioCalculator.constructor - 1');
this.accountBalanceItems = accountBalanceItems; this.accountBalanceItems = accountBalanceItems;
this.configurationService = configurationService; this.configurationService = configurationService;
this.currency = currency; this.currency = currency;
@ -138,9 +139,16 @@ export abstract class PortfolioCalculator {
this.endDate = endDate; this.endDate = endDate;
this.startDate = startDate; this.startDate = startDate;
console.timeEnd('--- PortfolioCalculator.constructor - 1');
console.time('--- PortfolioCalculator.constructor - 2');
this.computeTransactionPoints(); this.computeTransactionPoints();
console.timeEnd('--- PortfolioCalculator.constructor - 2');
console.time('--- PortfolioCalculator.constructor - 3');
this.snapshotPromise = this.initialize(); this.snapshotPromise = this.initialize();
console.timeEnd('--- PortfolioCalculator.constructor - 3');
} }
protected abstract calculateOverallPerformance( protected abstract calculateOverallPerformance(
@ -169,6 +177,7 @@ export abstract class PortfolioCalculator {
if (!transactionPoints.length) { if (!transactionPoints.length) {
return { return {
chartData: [],
currentValueInBaseCurrency: new Big(0), currentValueInBaseCurrency: new Big(0),
grossPerformance: new Big(0), grossPerformance: new Big(0),
grossPerformancePercentage: new Big(0), grossPerformancePercentage: new Big(0),
@ -290,6 +299,26 @@ export abstract class PortfolioCalculator {
const endDateString = format(endDate, DATE_FORMAT); const endDateString = format(endDate, DATE_FORMAT);
const chartStartDate = this.getStartDate();
const daysInMarket = differenceInDays(endDate, chartStartDate) + 1;
const step = true /*withDataDecimation*/
? Math.round(daysInMarket / Math.min(daysInMarket, MAX_CHART_ITEMS))
: 1;
let chartDates = eachDayOfInterval(
{ start: chartStartDate, end },
{ step }
).map((date) => {
return resetHours(date);
});
const includesEndDate = isSameDay(last(chartDates), end);
if (!includesEndDate) {
chartDates.push(resetHours(end));
}
if (firstIndex > 0) { if (firstIndex > 0) {
firstIndex--; firstIndex--;
} }
@ -299,6 +328,34 @@ export abstract class PortfolioCalculator {
const errors: ResponseError['errors'] = []; const errors: ResponseError['errors'] = [];
const accumulatedValuesByDate: {
[date: string]: {
investmentValueWithCurrencyEffect: Big;
totalCurrentValue: Big;
totalCurrentValueWithCurrencyEffect: Big;
totalInvestmentValue: Big;
totalInvestmentValueWithCurrencyEffect: Big;
totalNetPerformanceValue: Big;
totalNetPerformanceValueWithCurrencyEffect: Big;
totalTimeWeightedInvestmentValue: Big;
totalTimeWeightedInvestmentValueWithCurrencyEffect: Big;
};
} = {};
const valuesBySymbol: {
[symbol: string]: {
currentValues: { [date: string]: Big };
currentValuesWithCurrencyEffect: { [date: string]: Big };
investmentValuesAccumulated: { [date: string]: Big };
investmentValuesAccumulatedWithCurrencyEffect: { [date: string]: Big };
investmentValuesWithCurrencyEffect: { [date: string]: Big };
netPerformanceValues: { [date: string]: Big };
netPerformanceValuesWithCurrencyEffect: { [date: string]: Big };
timeWeightedInvestmentValues: { [date: string]: Big };
timeWeightedInvestmentValuesWithCurrencyEffect: { [date: string]: Big };
};
} = {};
for (const item of lastTransactionPoint.items) { for (const item of lastTransactionPoint.items) {
const marketPriceInBaseCurrency = ( const marketPriceInBaseCurrency = (
marketSymbolMap[endDateString]?.[item.symbol] ?? item.averagePrice marketSymbolMap[endDateString]?.[item.symbol] ?? item.averagePrice
@ -309,16 +366,25 @@ export abstract class PortfolioCalculator {
); );
const { const {
currentValues,
currentValuesWithCurrencyEffect,
grossPerformance, grossPerformance,
grossPerformancePercentage, grossPerformancePercentage,
grossPerformancePercentageWithCurrencyEffect, grossPerformancePercentageWithCurrencyEffect,
grossPerformanceWithCurrencyEffect, grossPerformanceWithCurrencyEffect,
hasErrors, hasErrors,
investmentValuesAccumulated,
investmentValuesAccumulatedWithCurrencyEffect,
investmentValuesWithCurrencyEffect,
netPerformance, netPerformance,
netPerformancePercentage, netPerformancePercentage,
netPerformancePercentageWithCurrencyEffect, netPerformancePercentageWithCurrencyEffect,
netPerformanceValues,
netPerformanceValuesWithCurrencyEffect,
netPerformanceWithCurrencyEffect, netPerformanceWithCurrencyEffect,
timeWeightedInvestment, timeWeightedInvestment,
timeWeightedInvestmentValues,
timeWeightedInvestmentValuesWithCurrencyEffect,
timeWeightedInvestmentWithCurrencyEffect, timeWeightedInvestmentWithCurrencyEffect,
totalDividend, totalDividend,
totalDividendInBaseCurrency, totalDividendInBaseCurrency,
@ -330,15 +396,29 @@ export abstract class PortfolioCalculator {
} = this.getSymbolMetrics({ } = this.getSymbolMetrics({
marketSymbolMap, marketSymbolMap,
start, start,
step,
dataSource: item.dataSource, dataSource: item.dataSource,
end: endDate, end: endDate,
exchangeRates: exchangeRates:
exchangeRatesByCurrency[`${item.currency}${this.currency}`], exchangeRatesByCurrency[`${item.currency}${this.currency}`],
isChartMode: true,
symbol: item.symbol symbol: item.symbol
}); });
hasAnySymbolMetricsErrors = hasAnySymbolMetricsErrors || hasErrors; hasAnySymbolMetricsErrors = hasAnySymbolMetricsErrors || hasErrors;
valuesBySymbol[item.symbol] = {
currentValues,
currentValuesWithCurrencyEffect,
investmentValuesAccumulated,
investmentValuesAccumulatedWithCurrencyEffect,
investmentValuesWithCurrencyEffect,
netPerformanceValues,
netPerformanceValuesWithCurrencyEffect,
timeWeightedInvestmentValues,
timeWeightedInvestmentValuesWithCurrencyEffect
};
positions.push({ positions.push({
dividend: totalDividend, dividend: totalDividend,
dividendInBaseCurrency: totalDividendInBaseCurrency, dividendInBaseCurrency: totalDividendInBaseCurrency,
@ -406,10 +486,143 @@ export abstract class PortfolioCalculator {
} }
} }
for (const currentDate of chartDates) {
const dateString = format(currentDate, DATE_FORMAT);
for (const symbol of Object.keys(valuesBySymbol)) {
const symbolValues = valuesBySymbol[symbol];
const currentValue =
symbolValues.currentValues?.[dateString] ?? new Big(0);
const currentValueWithCurrencyEffect =
symbolValues.currentValuesWithCurrencyEffect?.[dateString] ??
new Big(0);
const investmentValueAccumulated =
symbolValues.investmentValuesAccumulated?.[dateString] ?? new Big(0);
const investmentValueAccumulatedWithCurrencyEffect =
symbolValues.investmentValuesAccumulatedWithCurrencyEffect?.[
dateString
] ?? new Big(0);
const investmentValueWithCurrencyEffect =
symbolValues.investmentValuesWithCurrencyEffect?.[dateString] ??
new Big(0);
const netPerformanceValue =
symbolValues.netPerformanceValues?.[dateString] ?? new Big(0);
const netPerformanceValueWithCurrencyEffect =
symbolValues.netPerformanceValuesWithCurrencyEffect?.[dateString] ??
new Big(0);
const timeWeightedInvestmentValue =
symbolValues.timeWeightedInvestmentValues?.[dateString] ?? new Big(0);
const timeWeightedInvestmentValueWithCurrencyEffect =
symbolValues.timeWeightedInvestmentValuesWithCurrencyEffect?.[
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)
};
}
}
const chartData: HistoricalDataItem[] = Object.entries(
accumulatedValuesByDate
).map(([date, values]) => {
const {
investmentValueWithCurrencyEffect,
totalCurrentValue,
totalCurrentValueWithCurrencyEffect,
totalInvestmentValue,
totalInvestmentValueWithCurrencyEffect,
totalNetPerformanceValue,
totalNetPerformanceValueWithCurrencyEffect,
totalTimeWeightedInvestmentValue,
totalTimeWeightedInvestmentValueWithCurrencyEffect
} = values;
console.log(
'Chart: totalTimeWeightedInvestmentValue',
totalTimeWeightedInvestmentValue.toFixed()
);
const netPerformanceInPercentage = totalTimeWeightedInvestmentValue.eq(0)
? 0
: totalNetPerformanceValue
.div(totalTimeWeightedInvestmentValue)
.mul(100)
.toNumber();
const netPerformanceInPercentageWithCurrencyEffect =
totalTimeWeightedInvestmentValueWithCurrencyEffect.eq(0)
? 0
: totalNetPerformanceValueWithCurrencyEffect
.div(totalTimeWeightedInvestmentValueWithCurrencyEffect)
.mul(100)
.toNumber();
return {
date,
netPerformanceInPercentage,
netPerformanceInPercentageWithCurrencyEffect,
investmentValueWithCurrencyEffect:
investmentValueWithCurrencyEffect.toNumber(),
netPerformance: totalNetPerformanceValue.toNumber(),
netPerformanceWithCurrencyEffect:
totalNetPerformanceValueWithCurrencyEffect.toNumber(),
totalInvestment: totalInvestmentValue.toNumber(),
totalInvestmentValueWithCurrencyEffect:
totalInvestmentValueWithCurrencyEffect.toNumber(),
value: totalCurrentValue.toNumber(),
valueWithCurrencyEffect: totalCurrentValueWithCurrencyEffect.toNumber()
};
});
const overall = this.calculateOverallPerformance(positions); const overall = this.calculateOverallPerformance(positions);
return { return {
...overall, ...overall,
chartData,
errors, errors,
positions, positions,
totalInterestWithCurrencyEffect, totalInterestWithCurrencyEffect,
@ -426,6 +639,12 @@ export abstract class PortfolioCalculator {
dateRange?: DateRange; dateRange?: DateRange;
withDataDecimation?: boolean; withDataDecimation?: boolean;
}): Promise<HistoricalDataItem[]> { }): Promise<HistoricalDataItem[]> {
console.time('-------- PortfolioCalculator.getChart');
if (this.getTransactionPoints().length === 0) {
return [];
}
const { endDate, startDate } = getInterval(dateRange, this.getStartDate()); const { endDate, startDate } = getInterval(dateRange, this.getStartDate());
const daysInMarket = differenceInDays(endDate, startDate) + 1; const daysInMarket = differenceInDays(endDate, startDate) + 1;
@ -433,11 +652,15 @@ export abstract class PortfolioCalculator {
? Math.round(daysInMarket / Math.min(daysInMarket, MAX_CHART_ITEMS)) ? Math.round(daysInMarket / Math.min(daysInMarket, MAX_CHART_ITEMS))
: 1; : 1;
return this.getChartData({ const chartData = await this.getChartData({
step, step,
end: endDate, end: endDate,
start: startDate start: startDate
}); });
console.timeEnd('-------- PortfolioCalculator.getChart');
return chartData;
} }
public async getChartData({ public async getChartData({
@ -737,6 +960,11 @@ export abstract class PortfolioCalculator {
totalTimeWeightedInvestmentValueWithCurrencyEffect totalTimeWeightedInvestmentValueWithCurrencyEffect
} = values; } = values;
console.log(
'Chart: totalTimeWeightedInvestmentValue',
totalTimeWeightedInvestmentValue.toFixed()
);
const netPerformanceInPercentage = totalTimeWeightedInvestmentValue.eq(0) const netPerformanceInPercentage = totalTimeWeightedInvestmentValue.eq(0)
? 0 ? 0
: totalNetPerformanceValue : totalNetPerformanceValue
@ -848,8 +1076,11 @@ export abstract class PortfolioCalculator {
} }
public async getSnapshot() { public async getSnapshot() {
console.time('getSnapshot');
await this.snapshotPromise; await this.snapshotPromise;
console.timeEnd('getSnapshot');
return this.snapshot; return this.snapshot;
} }

6
apps/api/src/app/portfolio/calculator/twr/portfolio-calculator.ts

@ -98,6 +98,11 @@ export class TWRPortfolioCalculator extends PortfolioCalculator {
} }
} }
console.log(
'Overall: totalTimeWeightedInvestmentValue',
totalTimeWeightedInvestment.toFixed()
);
return { return {
currentValueInBaseCurrency, currentValueInBaseCurrency,
grossPerformance, grossPerformance,
@ -110,6 +115,7 @@ export class TWRPortfolioCalculator extends PortfolioCalculator {
totalInterestWithCurrencyEffect, totalInterestWithCurrencyEffect,
totalInvestment, totalInvestment,
totalInvestmentWithCurrencyEffect, totalInvestmentWithCurrencyEffect,
chartData: [],
netPerformancePercentage: totalTimeWeightedInvestment.eq(0) netPerformancePercentage: totalTimeWeightedInvestment.eq(0)
? new Big(0) ? new Big(0)
: netPerformance.div(totalTimeWeightedInvestment), : netPerformance.div(totalTimeWeightedInvestment),

12
apps/api/src/app/portfolio/portfolio.controller.ts

@ -81,6 +81,8 @@ export class PortfolioController {
@Query('tags') filterByTags?: string, @Query('tags') filterByTags?: string,
@Query('withMarkets') withMarketsParam = 'false' @Query('withMarkets') withMarketsParam = 'false'
): Promise<PortfolioDetails & { hasError: boolean }> { ): Promise<PortfolioDetails & { hasError: boolean }> {
console.time('TOTAL');
const withMarkets = withMarketsParam === 'true'; const withMarkets = withMarketsParam === 'true';
let hasDetails = true; let hasDetails = true;
@ -101,6 +103,8 @@ export class PortfolioController {
filterByTags filterByTags
}); });
console.time('- PortfolioController.getDetails - 1');
const { accounts, hasErrors, holdings, platforms, summary } = const { accounts, hasErrors, holdings, platforms, summary } =
await this.portfolioService.getDetails({ await this.portfolioService.getDetails({
dateRange, dateRange,
@ -111,6 +115,10 @@ export class PortfolioController {
withSummary: true withSummary: true
}); });
console.timeEnd('- PortfolioController.getDetails - 1');
console.time('- PortfolioController.getDetails - 2');
if (hasErrors || hasNotDefinedValuesInObject(holdings)) { if (hasErrors || hasNotDefinedValuesInObject(holdings)) {
hasError = true; hasError = true;
} }
@ -208,6 +216,10 @@ export class PortfolioController {
}; };
} }
console.timeEnd('- PortfolioController.getDetails - 2');
console.timeEnd('TOTAL');
return { return {
accounts, accounts,
hasError, hasError,

93
apps/api/src/app/portfolio/portfolio.service.ts

@ -339,6 +339,8 @@ export class PortfolioService {
withMarkets?: boolean; withMarkets?: boolean;
withSummary?: boolean; withSummary?: boolean;
}): Promise<PortfolioDetails & { hasErrors: boolean }> { }): Promise<PortfolioDetails & { hasErrors: boolean }> {
console.time('-- PortfolioService.getDetails - 1');
userId = await this.getUserId(impersonationId, userId); userId = await this.getUserId(impersonationId, userId);
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);
@ -365,9 +367,16 @@ export class PortfolioService {
this.request.user?.Settings.settings.isExperimentalFeatures this.request.user?.Settings.settings.isExperimentalFeatures
}); });
console.timeEnd('-- PortfolioService.getDetails - 1');
console.time('-- PortfolioService.getDetails - 2');
const { currentValueInBaseCurrency, hasErrors, positions } = const { currentValueInBaseCurrency, hasErrors, positions } =
await portfolioCalculator.getSnapshot(); await portfolioCalculator.getSnapshot();
console.timeEnd('-- PortfolioService.getDetails - 2');
console.time('-- PortfolioService.getDetails - 3');
const cashDetails = await this.accountService.getCashDetails({ const cashDetails = await this.accountService.getCashDetails({
filters, filters,
userId, userId,
@ -416,6 +425,9 @@ export class PortfolioService {
}; };
}); });
console.timeEnd('-- PortfolioService.getDetails - 3');
console.time('-- PortfolioService.getDetails - 4');
const [dataProviderResponses, symbolProfiles] = await Promise.all([ const [dataProviderResponses, symbolProfiles] = await Promise.all([
this.dataProviderService.getQuotes({ user, items: dataGatheringItems }), this.dataProviderService.getQuotes({ user, items: dataGatheringItems }),
this.symbolProfileService.getSymbolProfiles(dataGatheringItems) this.symbolProfileService.getSymbolProfiles(dataGatheringItems)
@ -431,6 +443,9 @@ export class PortfolioService {
portfolioItemsNow[position.symbol] = position; portfolioItemsNow[position.symbol] = position;
} }
console.timeEnd('-- PortfolioService.getDetails - 4');
console.time('-- PortfolioService.getDetails - 5');
for (const { for (const {
currency, currency,
dividend, dividend,
@ -571,6 +586,10 @@ export class PortfolioService {
}; };
} }
console.timeEnd('-- PortfolioService.getDetails - 5');
console.time('-- PortfolioService.getDetails - 6');
let summary: PortfolioSummary; let summary: PortfolioSummary;
if (withSummary) { if (withSummary) {
@ -589,6 +608,8 @@ export class PortfolioService {
}); });
} }
console.timeEnd('-- PortfolioService.getDetails - 6');
return { return {
accounts, accounts,
hasErrors, hasErrors,
@ -1051,15 +1072,20 @@ export class PortfolioService {
dateRange = 'max', dateRange = 'max',
filters, filters,
impersonationId, impersonationId,
portfolioCalculator,
userId, userId,
withExcludedAccounts = false withExcludedAccounts = false
}: { }: {
dateRange?: DateRange; dateRange?: DateRange;
filters?: Filter[]; filters?: Filter[];
impersonationId: string; impersonationId: string;
portfolioCalculator?: PortfolioCalculator;
userId: string; userId: string;
withExcludedAccounts?: boolean; withExcludedAccounts?: boolean;
}): Promise<PortfolioPerformanceResponse> { }): Promise<PortfolioPerformanceResponse> {
// OPTIMIZE (1.34s)
console.time('------ PortfolioService.getPerformance');
userId = await this.getUserId(impersonationId, userId); userId = await this.getUserId(impersonationId, userId);
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);
@ -1096,6 +1122,8 @@ export class PortfolioService {
const { endDate } = getInterval(dateRange); const { endDate } = getInterval(dateRange);
console.time('------- PortfolioService.getPerformance - 2');
const { activities } = await this.orderService.getOrders({ const { activities } = await this.orderService.getOrders({
endDate, endDate,
filters, filters,
@ -1104,6 +1132,9 @@ export class PortfolioService {
withExcludedAccounts withExcludedAccounts
}); });
console.timeEnd('------- PortfolioService.getPerformance - 2');
console.time('------- PortfolioService.getPerformance - 3');
if (accountBalanceItems?.length <= 0 && activities?.length <= 0) { if (accountBalanceItems?.length <= 0 && activities?.length <= 0) {
return { return {
chart: [], chart: [],
@ -1125,19 +1156,22 @@ export class PortfolioService {
}; };
} }
const portfolioCalculator = this.calculatorFactory.createCalculator({ portfolioCalculator =
accountBalanceItems, portfolioCalculator ??
activities, this.calculatorFactory.createCalculator({
dateRange, accountBalanceItems,
userId, activities,
calculationType: PerformanceCalculationType.TWR, dateRange,
currency: userCurrency, userId,
hasFilters: filters?.length > 0, calculationType: PerformanceCalculationType.TWR,
isExperimentalFeatures: currency: userCurrency,
this.request.user.Settings.settings.isExperimentalFeatures hasFilters: filters?.length > 0,
}); isExperimentalFeatures:
this.request.user.Settings.settings.isExperimentalFeatures
});
const { const {
chartData,
currentValueInBaseCurrency, currentValueInBaseCurrency,
errors, errors,
grossPerformance, grossPerformance,
@ -1152,6 +1186,9 @@ export class PortfolioService {
totalInvestment totalInvestment
} = await portfolioCalculator.getSnapshot(); } = await portfolioCalculator.getSnapshot();
console.timeEnd('------- PortfolioService.getPerformance - 3');
console.time('------- PortfolioService.getPerformance - 4');
let currentNetPerformance = netPerformance; let currentNetPerformance = netPerformance;
let currentNetPerformancePercentage = netPerformancePercentage; let currentNetPerformancePercentage = netPerformancePercentage;
@ -1164,11 +1201,13 @@ export class PortfolioService {
let currentNetWorth = 0; let currentNetWorth = 0;
const items = await portfolioCalculator.getChart({ /*const items = await portfolioCalculator.getChart({
dateRange dateRange
}); });*/
console.timeEnd('------- PortfolioService.getPerformance - 4');
console.time('------- PortfolioService.getPerformance - 5');
const itemOfToday = items.find(({ date }) => { const itemOfToday = chartData.find(({ date }) => {
return date === format(new Date(), DATE_FORMAT); return date === format(new Date(), DATE_FORMAT);
}); });
@ -1190,11 +1229,15 @@ export class PortfolioService {
currentNetWorth = itemOfToday.netWorth; currentNetWorth = itemOfToday.netWorth;
} }
console.timeEnd('------- PortfolioService.getPerformance - 5');
console.timeEnd('------ PortfolioService.getPerformance');
return { return {
errors, errors,
hasErrors, hasErrors,
chart: items, chart: chartData,
firstOrderDate: parseDate(items[0]?.date), firstOrderDate: parseDate(chartData[0]?.date),
performance: { performance: {
currentNetWorth, currentNetWorth,
currentValueInBaseCurrency: currentValueInBaseCurrency.toNumber(), currentValueInBaseCurrency: currentValueInBaseCurrency.toNumber(),
@ -1603,15 +1646,23 @@ export class PortfolioService {
userCurrency: string; userCurrency: string;
userId: string; userId: string;
}): Promise<PortfolioSummary> { }): Promise<PortfolioSummary> {
// OPTIMIZE (1.1 s)
console.time('---- PortfolioService.getSummary');
userId = await this.getUserId(impersonationId, userId); userId = await this.getUserId(impersonationId, userId);
const user = await this.userService.user({ id: userId }); const user = await this.userService.user({ id: userId });
console.time('----- PortfolioService.getSummary - 1');
const { activities } = await this.orderService.getOrders({ const { activities } = await this.orderService.getOrders({
userCurrency, userCurrency,
userId, userId,
withExcludedAccounts: true withExcludedAccounts: true
}); });
console.timeEnd('----- PortfolioService.getSummary - 1');
console.time('----- PortfolioService.getSummary - 2');
const excludedActivities: Activity[] = []; const excludedActivities: Activity[] = [];
const nonExcludedActivities: Activity[] = []; const nonExcludedActivities: Activity[] = [];
@ -1652,6 +1703,9 @@ export class PortfolioService {
const interest = await portfolioCalculator.getInterestInBaseCurrency(); const interest = await portfolioCalculator.getInterestInBaseCurrency();
console.timeEnd('----- PortfolioService.getSummary - 2');
console.time('----- PortfolioService.getSummary - 3');
const liabilities = const liabilities =
await portfolioCalculator.getLiabilitiesInBaseCurrency(); await portfolioCalculator.getLiabilitiesInBaseCurrency();
@ -1688,6 +1742,9 @@ export class PortfolioService {
}) })
); );
console.timeEnd('----- PortfolioService.getSummary - 3');
console.time('----- PortfolioService.getSummary - 4');
const cashDetailsWithExcludedAccounts = const cashDetailsWithExcludedAccounts =
await this.accountService.getCashDetails({ await this.accountService.getCashDetails({
userId, userId,
@ -1725,6 +1782,10 @@ export class PortfolioService {
) )
})?.toNumber(); })?.toNumber();
console.timeEnd('----- PortfolioService.getSummary - 4');
console.timeEnd('---- PortfolioService.getSummary');
return { return {
annualizedPerformancePercent, annualizedPerformancePercent,
annualizedPerformancePercentWithCurrencyEffect, annualizedPerformancePercentWithCurrencyEffect,

5
libs/common/src/lib/models/portfolio-snapshot.ts

@ -1,14 +1,17 @@
import { transformToBig } from '@ghostfolio/common/class-transformer'; import { transformToBig } from '@ghostfolio/common/class-transformer';
import { UniqueAsset } from '@ghostfolio/common/interfaces'; import { HistoricalDataItem, UniqueAsset } from '@ghostfolio/common/interfaces';
import { TimelinePosition } from '@ghostfolio/common/models'; import { TimelinePosition } from '@ghostfolio/common/models';
import { Big } from 'big.js'; import { Big } from 'big.js';
import { Transform, Type } from 'class-transformer'; import { Transform, Type } from 'class-transformer';
export class PortfolioSnapshot { export class PortfolioSnapshot {
chartData: HistoricalDataItem[];
@Transform(transformToBig, { toClassOnly: true }) @Transform(transformToBig, { toClassOnly: true })
@Type(() => Big) @Type(() => Big)
currentValueInBaseCurrency: Big; currentValueInBaseCurrency: Big;
errors?: UniqueAsset[]; errors?: UniqueAsset[];
@Transform(transformToBig, { toClassOnly: true }) @Transform(transformToBig, { toClassOnly: true })

Loading…
Cancel
Save