diff --git a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btcusd.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btcusd.spec.ts index b8cecb350..a10a6da4e 100644 --- a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btcusd.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btcusd.spec.ts @@ -7,6 +7,7 @@ import { import { PortfolioCalculatorFactory } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator.factory'; import { CurrentRateService } from '@ghostfolio/api/app/portfolio/current-rate.service'; import { CurrentRateServiceMock } from '@ghostfolio/api/app/portfolio/current-rate.service.mock'; +import { getChartByYear } from '@ghostfolio/api/app/portfolio/portfolio-chart.helper'; import { RedisCacheService } from '@ghostfolio/api/app/redis-cache/redis-cache.service'; import { RedisCacheServiceMock } from '@ghostfolio/api/app/redis-cache/redis-cache.service.mock'; import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; @@ -88,6 +89,10 @@ describe('PortfolioCalculator', () => { ); }); + afterEach(() => { + jest.useRealTimers(); + }); + describe('get current positions', () => { it.only('with BTCUSD buy (in USD)', async () => { jest.useFakeTimers().setSystemTime(parseDate('2022-01-14').getTime()); @@ -258,5 +263,65 @@ describe('PortfolioCalculator', () => { { date: '2022-01-01', investment: 0 } ]); }); + + it.only('with BTCUSD buy - performance grouped by year', async () => { + jest.useFakeTimers().setSystemTime(parseDate('2022-01-14').getTime()); + + const activities: Activity[] = exportResponse.activities.map( + (activity) => ({ + ...activityDummyData, + ...activity, + date: parseDate(activity.date), + feeInAssetProfileCurrency: 4.46, + feeInBaseCurrency: 4.46, + SymbolProfile: { + ...symbolProfileDummyData, + currency: 'USD', + dataSource: activity.dataSource, + name: 'Bitcoin', + symbol: activity.symbol + }, + unitPriceInAssetProfileCurrency: 44558.42 + }) + ); + + const portfolioCalculator = portfolioCalculatorFactory.createCalculator({ + activities, + calculationType: PerformanceCalculationType.ROAI, + currency: exportResponse.user.settings.currency, + userId: userDummyData.id + }); + + await portfolioCalculator.computeSnapshot(); + + const { chart } = await portfolioCalculator.getPerformance({ + end: parseDate('2022-01-14'), + start: parseDate('2021-12-11') + }); + + const chartByYear = getChartByYear(chart); + + // Chart spans from 2021-12-11 to 2022-01-14, covering two years + expect(chartByYear).toHaveLength(2); + + // First year (2021) - should have the last data point from 2021-12-31 + expect(chartByYear[0].date).toEqual('2021-01-01'); + expect(chartByYear[0]).toMatchObject( + expect.objectContaining({ + date: '2021-01-01', + totalInvestmentValueWithCurrencyEffect: 44558.42 + }) + ); + + // Second year (2022) - should have the last data point from 2022-01-14 + expect(chartByYear[1].date).toEqual('2022-01-01'); + expect(chartByYear[1]).toMatchObject( + expect.objectContaining({ + date: '2022-01-01', + netPerformance: -1463.18, + totalInvestmentValueWithCurrencyEffect: 44558.42 + }) + ); + }); }); }); diff --git a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-googl-buy.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-googl-buy.spec.ts index e438d9c6d..46cc163a9 100644 --- a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-googl-buy.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-googl-buy.spec.ts @@ -6,6 +6,7 @@ import { import { PortfolioCalculatorFactory } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator.factory'; import { CurrentRateService } from '@ghostfolio/api/app/portfolio/current-rate.service'; import { CurrentRateServiceMock } from '@ghostfolio/api/app/portfolio/current-rate.service.mock'; +import { getChartByYear } from '@ghostfolio/api/app/portfolio/portfolio-chart.helper'; import { RedisCacheService } from '@ghostfolio/api/app/redis-cache/redis-cache.service'; import { RedisCacheServiceMock } from '@ghostfolio/api/app/redis-cache/redis-cache.service.mock'; import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; @@ -90,6 +91,10 @@ describe('PortfolioCalculator', () => { ); }); + afterEach(() => { + jest.useRealTimers(); + }); + describe('get current positions', () => { it.only('with GOOGL buy', async () => { jest.useFakeTimers().setSystemTime(parseDate('2023-07-10').getTime()); @@ -230,5 +235,57 @@ describe('PortfolioCalculator', () => { { date: '2023-01-01', investment: 82.329056 } ]); }); + + it.only('with GOOGL buy - performance grouped by year', async () => { + jest.useFakeTimers().setSystemTime(parseDate('2023-07-10').getTime()); + + const activities: Activity[] = [ + { + ...activityDummyData, + date: new Date('2023-01-03'), + feeInAssetProfileCurrency: 1, + feeInBaseCurrency: 0.9238, + quantity: 1, + SymbolProfile: { + ...symbolProfileDummyData, + currency: 'USD', + dataSource: 'YAHOO', + name: 'Alphabet Inc.', + symbol: 'GOOGL' + }, + type: 'BUY', + unitPriceInAssetProfileCurrency: 89.12 + } + ]; + + const portfolioCalculator = portfolioCalculatorFactory.createCalculator({ + activities, + calculationType: PerformanceCalculationType.ROAI, + currency: 'CHF', + userId: userDummyData.id + }); + + await portfolioCalculator.computeSnapshot(); + + const { chart } = await portfolioCalculator.getPerformance({ + end: parseDate('2023-07-10'), + start: parseDate('2023-01-03') + }); + + const chartByYear = getChartByYear(chart); + + // All data is within 2023, so should only have one year entry + expect(chartByYear).toHaveLength(1); + + // Should have the last data point of 2023 (2023-07-10) with normalized date + expect(chartByYear[0].date).toEqual('2023-01-01'); + expect(chartByYear[0]).toMatchObject( + expect.objectContaining({ + date: '2023-01-01', + netPerformance: new Big('26.33').mul(0.8854).toNumber(), + totalInvestmentValueWithCurrencyEffect: 82.329056 + }) + ); + }); }); }); diff --git a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-novn-buy-and-sell.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-novn-buy-and-sell.spec.ts index c4850db66..e13979653 100644 --- a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-novn-buy-and-sell.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-novn-buy-and-sell.spec.ts @@ -7,6 +7,7 @@ import { import { PortfolioCalculatorFactory } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator.factory'; import { CurrentRateService } from '@ghostfolio/api/app/portfolio/current-rate.service'; import { CurrentRateServiceMock } from '@ghostfolio/api/app/portfolio/current-rate.service.mock'; +import { getChartByYear } from '@ghostfolio/api/app/portfolio/portfolio-chart.helper'; import { RedisCacheService } from '@ghostfolio/api/app/redis-cache/redis-cache.service'; import { RedisCacheServiceMock } from '@ghostfolio/api/app/redis-cache/redis-cache.service.mock'; import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; @@ -91,6 +92,10 @@ describe('PortfolioCalculator', () => { ); }); + afterEach(() => { + jest.useRealTimers(); + }); + describe('get current positions', () => { it.only('with NOVN.SW buy and sell', async () => { jest.useFakeTimers().setSystemTime(parseDate('2022-04-11').getTime()); @@ -261,5 +266,57 @@ describe('PortfolioCalculator', () => { { date: '2022-01-01', investment: 0 } ]); }); + + it.only('with NOVN.SW buy and sell - performance grouped by year', async () => { + jest.useFakeTimers().setSystemTime(parseDate('2022-04-11').getTime()); + + const activities: Activity[] = exportResponse.activities.map( + (activity) => ({ + ...activityDummyData, + ...activity, + date: parseDate(activity.date), + feeInAssetProfileCurrency: activity.fee, + feeInBaseCurrency: activity.fee, + SymbolProfile: { + ...symbolProfileDummyData, + currency: activity.currency, + dataSource: activity.dataSource, + name: 'Novartis AG', + symbol: activity.symbol + }, + unitPriceInAssetProfileCurrency: activity.unitPrice + }) + ); + + const portfolioCalculator = portfolioCalculatorFactory.createCalculator({ + activities, + calculationType: PerformanceCalculationType.ROAI, + currency: exportResponse.user.settings.currency, + userId: userDummyData.id + }); + + await portfolioCalculator.computeSnapshot(); + + const { chart } = await portfolioCalculator.getPerformance({ + end: parseDate('2022-04-11'), + start: parseDate('2022-03-06') + }); + + const chartByYear = getChartByYear(chart); + + // All data is within 2022, so should only have one year entry + expect(chartByYear).toHaveLength(1); + + // Should have the last data point of 2022 (2022-04-11) with normalized date + expect(chartByYear[0].date).toEqual('2022-01-01'); + expect(chartByYear[0]).toMatchObject( + expect.objectContaining({ + date: '2022-01-01', + netPerformance: 19.86, + netPerformanceInPercentage: 0.13100263852242744, + totalInvestmentValueWithCurrencyEffect: 0 + }) + ); + }); }); });