diff --git a/apps/api/src/app/portfolio/portfolio-calculator.ts b/apps/api/src/app/portfolio/portfolio-calculator.ts index 00b0b522b..8b8124280 100644 --- a/apps/api/src/app/portfolio/portfolio-calculator.ts +++ b/apps/api/src/app/portfolio/portfolio-calculator.ts @@ -390,13 +390,11 @@ export class PortfolioCalculator { ? format(previousDate, DATE_FORMAT) : null; let totalCurrentValue = new Big(0); - let maxTotalInvestmentValue = new Big(0); let previousTotalInvestmentValue = new Big(0); - let timeWeightedPerformance = new Big(0); if (calculateTimeWeightedPerformance && previousDateString) { previousTotalInvestmentValue = - accumulatedValuesByDate[previousDateString].totalInvestmentValue; + accumulatedValuesByDate[previousDateString].totalCurrentValue; } for (const symbol of Object.keys(valuesBySymbol)) { @@ -406,6 +404,8 @@ export class PortfolioCalculator { totalCurrentValue = totalCurrentValue.plus(symbolCurrentValues); + let timeWeightedPerformanceContribution = new Big(0); + if ( previousTotalInvestmentValue.toNumber() && symbolValues.netPerformanceValuesPercentage && @@ -418,26 +418,16 @@ export class PortfolioCalculator { const netPerformance = symbolValues.netPerformanceValuesPercentage?.[dateString] ?? new Big(0); - const timeWeightedPerformanceContribution = previousValue + timeWeightedPerformanceContribution = previousValue .div(previousTotalInvestmentValue) - .mul(netPerformance) - .mul(100); - timeWeightedPerformance = timeWeightedPerformance.plus( - timeWeightedPerformanceContribution - ); + .mul(netPerformance); } - - let totalTimeWeightedPerformance = timeWeightedPerformance.plus( - accumulatedValuesByDate[previousDateString] - ?.totalTimeWeightedPerformance ?? new Big(0) - ); - accumulatedValuesByDate = this.accumulatedValuesByDate( valuesBySymbol, symbol, dateString, accumulatedValuesByDate, - totalTimeWeightedPerformance + timeWeightedPerformanceContribution ); } @@ -453,6 +443,19 @@ export class PortfolioCalculator { totalNetPerformanceValue } = accumulatedValuesByDate[dateString]; + let totalNetTimeWeightedPerformance = new Big(0); + + if (previousDateString) { + totalNetTimeWeightedPerformance = ( + accumulatedValuesByDate[previousDateString] + ?.totalTimeWeightedPerformance ?? new Big(0) + ) + .plus(1) + .mul(totalTimeWeightedPerformance.plus(1)) + .minus(1) + .mul(100); + } + const netPerformanceInPercentage = totalTimeWeightedInvestmentValue.eq(0) ? 0 : totalNetPerformanceValue @@ -476,7 +479,7 @@ export class PortfolioCalculator { totalInvestment: totalInvestmentValue.toNumber(), value: totalCurrentValue.toNumber(), valueWithCurrencyEffect: totalCurrentValueWithCurrencyEffect.toNumber(), - timeWeightedPerformance: totalTimeWeightedPerformance.toNumber(), + timeWeightedPerformance: totalNetTimeWeightedPerformance.toNumber(), investmentValueWithCurrencyEffect: investmentValueWithCurrencyEffect.toNumber(), netPerformanceWithCurrencyEffect: @@ -589,7 +592,10 @@ export class PortfolioCalculator { accumulatedValuesByDate[dateString] ?.totalTimeWeightedInvestmentValueWithCurrencyEffect ?? new Big(0) ).add(timeWeightedInvestmentValueWithCurrencyEffect), - totalTimeWeightedPerformance: timeWeightedPerformance + totalTimeWeightedPerformance: ( + accumulatedValuesByDate[dateString]?.totalTimeWeightedPerformance ?? + new Big(0) + ).add(timeWeightedPerformance) }; return accumulatedValuesByDate; @@ -1162,78 +1168,55 @@ export class PortfolioCalculator { symbol: string; calculatePerformance?: boolean; }): SymbolMetrics { - const currentExchangeRate = exchangeRates[format(new Date(), DATE_FORMAT)]; - const currentValues: WithCurrencyEffect<{ [date: string]: Big }> = { - Value: {}, - WithCurrencyEffect: {} - }; - let fees: WithCurrencyEffect = { - Value: new Big(0), - WithCurrencyEffect: new Big(0) - }; - let feesAtStartDate: WithCurrencyEffect = { - Value: new Big(0), - WithCurrencyEffect: new Big(0) - }; - let grossPerformance: WithCurrencyEffect = { - Value: new Big(0), - WithCurrencyEffect: new Big(0) - }; - let grossPerformanceAtStartDate: WithCurrencyEffect = { - Value: new Big(0), - WithCurrencyEffect: new Big(0) - }; - let grossPerformanceFromSells: WithCurrencyEffect = { - Value: new Big(0), - WithCurrencyEffect: new Big(0) - }; - let averagePriceAtEndDate = new Big(0); - let averagePriceAtStartDate = new Big(0); - const investmentValues: WithCurrencyEffect<{ [date: string]: Big }> = { - Value: {}, - WithCurrencyEffect: {} - }; - const maxInvestmentValues: { [date: string]: Big } = {}; - let maxTotalInvestment = new Big(0); - const netPerformanceValuesPercentage: { [date: string]: Big } = {}; - let initialValue; - let investmentAtStartDate; - const investmentValuesAccumulated: WithCurrencyEffect<{ - [date: string]: Big; - }> = { - Value: {}, - WithCurrencyEffect: {} - }; - let lastAveragePrice: WithCurrencyEffect = { - Value: new Big(0), - WithCurrencyEffect: new Big(0) - }; - const netPerformanceValues: WithCurrencyEffect<{ [date: string]: Big }> = { - Value: {}, - WithCurrencyEffect: {} - }; - const timeWeightedInvestmentValues: WithCurrencyEffect<{ - [date: string]: Big; - }> = { - Value: {}, - WithCurrencyEffect: {} - }; - - let totalInvestment: WithCurrencyEffect = { - Value: new Big(0), - WithCurrencyEffect: new Big(0) - }; - - let totalInvestmentWithGrossPerformanceFromSell: WithCurrencyEffect = { - Value: new Big(0), - WithCurrencyEffect: new Big(0) - }; + let { + averagePriceAtStartDate, + totalUnits, + totalInvestment, + investmentAtStartDate, + valueAtStartDate, + maxTotalInvestment, + averagePriceAtEndDate, + initialValue, + fees, + feesAtStartDate, + lastAveragePrice, + grossPerformanceFromSells, + totalInvestmentWithGrossPerformanceFromSell, + grossPerformance, + grossPerformanceAtStartDate, + currentValues, + netPerformanceValues, + netPerformanceValuesPercentage, + investmentValues, + investmentValuesAccumulated, + maxInvestmentValues, + timeWeightedInvestmentValues + }: { + averagePriceAtStartDate: Big; + totalUnits: Big; + totalInvestment: WithCurrencyEffect; + investmentAtStartDate: any; + valueAtStartDate: WithCurrencyEffect; + maxTotalInvestment: Big; + averagePriceAtEndDate: Big; + initialValue: any; + fees: WithCurrencyEffect; + feesAtStartDate: WithCurrencyEffect; + lastAveragePrice: WithCurrencyEffect; + grossPerformanceFromSells: WithCurrencyEffect; + totalInvestmentWithGrossPerformanceFromSell: WithCurrencyEffect; + grossPerformance: WithCurrencyEffect; + grossPerformanceAtStartDate: WithCurrencyEffect; + currentValues: WithCurrencyEffect<{ [date: string]: Big }>; + netPerformanceValues: WithCurrencyEffect<{ [date: string]: Big }>; + netPerformanceValuesPercentage: { [date: string]: Big }; + investmentValues: WithCurrencyEffect<{ [date: string]: Big }>; + investmentValuesAccumulated: WithCurrencyEffect<{ [date: string]: Big }>; + maxInvestmentValues: { [date: string]: Big }; + timeWeightedInvestmentValues: WithCurrencyEffect<{ [date: string]: Big }>; + } = this.InitializeSymbolMetricValues(); - let totalUnits = new Big(0); - let valueAtStartDate: WithCurrencyEffect = { - Value: new Big(0), - WithCurrencyEffect: new Big(0) - }; + const currentExchangeRate = exchangeRates[format(new Date(), DATE_FORMAT)]; // Clone orders to keep the original values in this.orders let orders: PortfolioOrderItem[] = cloneDeep(this.orders).filter( @@ -1426,6 +1409,104 @@ export class PortfolioCalculator { }; } + private InitializeSymbolMetricValues() { + const currentValues: WithCurrencyEffect<{ [date: string]: Big }> = { + Value: {}, + WithCurrencyEffect: {} + }; + let fees: WithCurrencyEffect = { + Value: new Big(0), + WithCurrencyEffect: new Big(0) + }; + let feesAtStartDate: WithCurrencyEffect = { + Value: new Big(0), + WithCurrencyEffect: new Big(0) + }; + let grossPerformance: WithCurrencyEffect = { + Value: new Big(0), + WithCurrencyEffect: new Big(0) + }; + let grossPerformanceAtStartDate: WithCurrencyEffect = { + Value: new Big(0), + WithCurrencyEffect: new Big(0) + }; + let grossPerformanceFromSells: WithCurrencyEffect = { + Value: new Big(0), + WithCurrencyEffect: new Big(0) + }; + let averagePriceAtEndDate = new Big(0); + let averagePriceAtStartDate = new Big(0); + const investmentValues: WithCurrencyEffect<{ [date: string]: Big }> = { + Value: {}, + WithCurrencyEffect: {} + }; + const maxInvestmentValues: { [date: string]: Big } = {}; + let maxTotalInvestment = new Big(0); + const netPerformanceValuesPercentage: { [date: string]: Big } = {}; + let initialValue; + let investmentAtStartDate; + const investmentValuesAccumulated: WithCurrencyEffect<{ + [date: string]: Big; + }> = { + Value: {}, + WithCurrencyEffect: {} + }; + let lastAveragePrice: WithCurrencyEffect = { + Value: new Big(0), + WithCurrencyEffect: new Big(0) + }; + const netPerformanceValues: WithCurrencyEffect<{ [date: string]: Big }> = { + Value: {}, + WithCurrencyEffect: {} + }; + const timeWeightedInvestmentValues: WithCurrencyEffect<{ + [date: string]: Big; + }> = { + Value: {}, + WithCurrencyEffect: {} + }; + + let totalInvestment: WithCurrencyEffect = { + Value: new Big(0), + WithCurrencyEffect: new Big(0) + }; + + let totalInvestmentWithGrossPerformanceFromSell: WithCurrencyEffect = { + Value: new Big(0), + WithCurrencyEffect: new Big(0) + }; + + let totalUnits = new Big(0); + let valueAtStartDate: WithCurrencyEffect = { + Value: new Big(0), + WithCurrencyEffect: new Big(0) + }; + return { + averagePriceAtStartDate, + totalUnits, + totalInvestment, + investmentAtStartDate, + valueAtStartDate, + maxTotalInvestment, + averagePriceAtEndDate, + initialValue, + fees, + feesAtStartDate, + lastAveragePrice, + grossPerformanceFromSells, + totalInvestmentWithGrossPerformanceFromSell, + grossPerformance, + grossPerformanceAtStartDate, + currentValues, + netPerformanceValues, + netPerformanceValuesPercentage, + investmentValues, + investmentValuesAccumulated, + maxInvestmentValues, + timeWeightedInvestmentValues + }; + } + @LogPerformance private calculatePerformanceOfSymbol( orders: PortfolioOrderItem[],