diff --git a/apps/api/src/app/portfolio/calculator/portfolio-calculator-test-utils.ts b/apps/api/src/app/portfolio/calculator/portfolio-calculator-test-utils.ts index 2dee4cbc89..87474e806c 100644 --- a/apps/api/src/app/portfolio/calculator/portfolio-calculator-test-utils.ts +++ b/apps/api/src/app/portfolio/calculator/portfolio-calculator-test-utils.ts @@ -1,4 +1,10 @@ -import { ExportResponse } from '@ghostfolio/common/interfaces'; +import { PortfolioCalculator } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator'; +import { getIntervalFromDateRange } from '@ghostfolio/common/calculation-helper'; +import { + ExportResponse, + HistoricalDataItem +} from '@ghostfolio/common/interfaces'; +import { DateRange } from '@ghostfolio/common/types'; import { readFileSync } from 'node:fs'; @@ -38,6 +44,33 @@ export const userDummyData = { id: 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' }; +/** + * Returns the last chart item of getPerformance() for each date range + */ +export async function getPerformanceByDateRange({ + dateRanges, + portfolioCalculator +}: { + dateRanges: DateRange[]; + portfolioCalculator: PortfolioCalculator; +}): Promise<{ [dateRange: string]: HistoricalDataItem }> { + const performanceByDateRange: { [dateRange: string]: HistoricalDataItem } = + {}; + + for (const dateRange of dateRanges) { + const { endDate, startDate } = getIntervalFromDateRange({ dateRange }); + + const { chart } = await portfolioCalculator.getPerformance({ + end: endDate, + start: startDate + }); + + performanceByDateRange[dateRange] = chart.at(-1); + } + + return performanceByDateRange; +} + export function loadExportFile(filePath: string): ExportResponse { return JSON.parse(readFileSync(filePath, 'utf8')); } diff --git a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy-and-sell.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy-and-sell.spec.ts index f054410ae3..d16b9be6f6 100644 --- a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy-and-sell.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy-and-sell.spec.ts @@ -1,6 +1,7 @@ import { activityDummyData, assetProfileDummyData, + getPerformanceByDateRange, userDummyData } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator-test-utils'; import { PortfolioCalculatorFactory } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator.factory'; @@ -124,11 +125,17 @@ describe('PortfolioCalculator', () => { activities, calculationType: PerformanceCalculationType.ROAI, currency: 'CHF', + usePortfolioSnapshotCache: false, userId: userDummyData.id }); const portfolioSnapshot = await portfolioCalculator.computeSnapshot(); + const performanceByDateRange = await getPerformanceByDateRange({ + portfolioCalculator, + dateRanges: ['1d', 'max', 'ytd'] + }); + const investments = portfolioCalculator.getInvestments(); const investmentsByMonth = portfolioCalculator.getInvestmentsByGroup({ @@ -213,6 +220,36 @@ describe('PortfolioCalculator', () => { expect(investmentsByYear).toEqual([ { date: '2021-01-01', investment: 0 } ]); + + expect(performanceByDateRange).toMatchObject({ + '1d': { + date: '2021-12-18', + netPerformance: 0, + netPerformanceInPercentage: 0, + netPerformanceInPercentageWithCurrencyEffect: 0, + netPerformanceWithCurrencyEffect: 0, + totalInvestmentValueWithCurrencyEffect: 0, + valueWithCurrencyEffect: 0 + }, + max: { + date: '2021-12-18', + netPerformance: -15.8, + netPerformanceInPercentage: -0.05528341497550735, + netPerformanceInPercentageWithCurrencyEffect: -0.05528341497550735, + netPerformanceWithCurrencyEffect: -15.8, + totalInvestmentValueWithCurrencyEffect: 0, + valueWithCurrencyEffect: 0 + }, + ytd: { + date: '2021-12-18', + netPerformance: -15.8, + netPerformanceInPercentage: -0.05528341497550735, + netPerformanceInPercentageWithCurrencyEffect: -0.05528341497550735, + netPerformanceWithCurrencyEffect: -15.8, + totalInvestmentValueWithCurrencyEffect: 0, + valueWithCurrencyEffect: 0 + } + }); }); }); }); diff --git a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btcusd-buy-and-sell-partially.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btcusd-buy-and-sell-partially.spec.ts index c775e81be7..e54fd96aa7 100644 --- a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btcusd-buy-and-sell-partially.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btcusd-buy-and-sell-partially.spec.ts @@ -1,6 +1,7 @@ import { activityDummyData, assetProfileDummyData, + getPerformanceByDateRange, userDummyData } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator-test-utils'; import { PortfolioCalculatorFactory } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator.factory'; @@ -136,11 +137,17 @@ describe('PortfolioCalculator', () => { activities, calculationType: PerformanceCalculationType.ROAI, currency: 'CHF', + usePortfolioSnapshotCache: false, userId: userDummyData.id }); const portfolioSnapshot = await portfolioCalculator.computeSnapshot(); + const performanceByDateRange = await getPerformanceByDateRange({ + portfolioCalculator, + dateRanges: ['1d', 'max', 'ytd'] + }); + const investments = portfolioCalculator.getInvestments(); const investmentsByMonth = portfolioCalculator.getInvestmentsByGroup({ @@ -269,6 +276,36 @@ describe('PortfolioCalculator', () => { { date: '2017-01-01', investment: -318.54266729999995 }, { date: '2018-01-01', investment: 0 } ]); + + expect(performanceByDateRange).toMatchObject({ + '1d': { + date: '2018-01-01', + netPerformance: -486.0860160000011, + netPerformanceInPercentage: -1.5259683109961866, + netPerformanceInPercentageWithCurrencyEffect: -1.7468407881319952, + netPerformanceWithCurrencyEffect: -556.4433239999998, + totalInvestmentValueWithCurrencyEffect: 318.54266729999995, + valueWithCurrencyEffect: 13298.425356 + }, + max: { + date: '2018-01-01', + netPerformance: 26458.9121202, + netPerformanceInPercentage: 41.63298219956282, + netPerformanceInPercentageWithCurrencyEffect: 41.72313811883715, + netPerformanceWithCurrencyEffect: 26516.2087014, + totalInvestmentValueWithCurrencyEffect: 318.54266729999995, + valueWithCurrencyEffect: 13298.425356 + }, + ytd: { + date: '2018-01-01', + netPerformance: -486.0860160000011, + netPerformanceInPercentage: -1.5259683109961866, + netPerformanceInPercentageWithCurrencyEffect: -1.7468407881319952, + netPerformanceWithCurrencyEffect: -556.4433239999998, + totalInvestmentValueWithCurrencyEffect: 318.54266729999995, + valueWithCurrencyEffect: 13298.425356 + } + }); }); }); }); diff --git a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-msft-buy-with-dividend.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-msft-buy-with-dividend.spec.ts index 39994a8e58..bb16af5ef6 100644 --- a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-msft-buy-with-dividend.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-msft-buy-with-dividend.spec.ts @@ -1,6 +1,7 @@ import { activityDummyData, assetProfileDummyData, + getPerformanceByDateRange, userDummyData } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator-test-utils'; import { PortfolioCalculatorFactory } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator.factory'; @@ -124,11 +125,17 @@ describe('PortfolioCalculator', () => { activities, calculationType: PerformanceCalculationType.ROAI, currency: 'USD', + usePortfolioSnapshotCache: false, userId: userDummyData.id }); const portfolioSnapshot = await portfolioCalculator.computeSnapshot(); + const performanceByDateRange = await getPerformanceByDateRange({ + portfolioCalculator, + dateRanges: ['1d', 'max', 'ytd'] + }); + expect(portfolioSnapshot).toMatchObject({ errors: [], hasErrors: false, @@ -181,6 +188,36 @@ describe('PortfolioCalculator', () => { totalInvestmentValueWithCurrencyEffect: 298.58 }) ); + + expect(performanceByDateRange).toMatchObject({ + '1d': { + date: '2023-07-10', + netPerformance: -5.390000000000001, + netPerformanceInPercentage: -0.018052113336459244, + netPerformanceInPercentageWithCurrencyEffect: -0.018052113336459244, + netPerformanceWithCurrencyEffect: -5.390000000000001, + totalInvestmentValueWithCurrencyEffect: 298.58, + valueWithCurrencyEffect: 331.83 + }, + max: { + date: '2023-07-10', + netPerformance: 14.25, + netPerformanceInPercentage: 0.04772590260566659, + netPerformanceInPercentageWithCurrencyEffect: 0.04772590260566659, + netPerformanceWithCurrencyEffect: 14.25, + totalInvestmentValueWithCurrencyEffect: 298.58, + valueWithCurrencyEffect: 331.83 + }, + ytd: { + date: '2023-07-10', + netPerformance: -7.68, + netPerformanceInPercentage: -0.025721749614843515, + netPerformanceInPercentageWithCurrencyEffect: -0.025721749614843515, + netPerformanceWithCurrencyEffect: -7.68, + totalInvestmentValueWithCurrencyEffect: 298.58, + valueWithCurrencyEffect: 331.83 + } + }); }); }); });