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 051b75e9b..0f68c1358 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 @@ -526,5 +526,168 @@ describe('PortfolioCalculator', () => { expect(portfolioSnapshot).toHaveProperty('annualizedDividendYield'); expect(portfolioSnapshot.annualizedDividendYield).toBeCloseTo(0.0184, 4); }); + + it('ignores dividends older than 12 months when aggregating portfolio yield', async () => { + jest.useFakeTimers().setSystemTime(parseDate('2023-07-10').getTime()); + + const activities: Activity[] = [ + // MSFT: 1 share @ 300, 3 dividends total (one older than 12 months) + { + ...activityDummyData, + date: new Date('2021-09-16'), + feeInAssetProfileCurrency: 0, + feeInBaseCurrency: 0, + quantity: 1, + SymbolProfile: { + ...symbolProfileDummyData, + currency: 'USD', + dataSource: 'YAHOO', + name: 'Microsoft Inc.', + symbol: 'MSFT' + }, + type: 'BUY', + unitPriceInAssetProfileCurrency: 300 + }, + { + ...activityDummyData, + date: new Date('2021-11-16'), + feeInAssetProfileCurrency: 0, + feeInBaseCurrency: 0, + quantity: 1, + SymbolProfile: { + ...symbolProfileDummyData, + currency: 'USD', + dataSource: 'YAHOO', + name: 'Microsoft Inc.', + symbol: 'MSFT' + }, + type: 'DIVIDEND', + unitPriceInAssetProfileCurrency: 0.62 + }, + { + ...activityDummyData, + date: new Date('2022-08-16'), + feeInAssetProfileCurrency: 0, + feeInBaseCurrency: 0, + quantity: 1, + SymbolProfile: { + ...symbolProfileDummyData, + currency: 'USD', + dataSource: 'YAHOO', + name: 'Microsoft Inc.', + symbol: 'MSFT' + }, + type: 'DIVIDEND', + unitPriceInAssetProfileCurrency: 0.65 + }, + { + ...activityDummyData, + date: new Date('2023-05-16'), + feeInAssetProfileCurrency: 0, + feeInBaseCurrency: 0, + quantity: 1, + SymbolProfile: { + ...symbolProfileDummyData, + currency: 'USD', + dataSource: 'YAHOO', + name: 'Microsoft Inc.', + symbol: 'MSFT' + }, + type: 'DIVIDEND', + unitPriceInAssetProfileCurrency: 0.65 + }, + // IBM: 1 share @ 200, 2 dividends total (one older than 12 months) + { + ...activityDummyData, + date: new Date('2021-10-01'), + feeInAssetProfileCurrency: 0, + feeInBaseCurrency: 0, + quantity: 1, + SymbolProfile: { + ...symbolProfileDummyData, + currency: 'USD', + dataSource: 'YAHOO', + name: 'IBM', + symbol: 'IBM' + }, + type: 'BUY', + unitPriceInAssetProfileCurrency: 200 + }, + { + ...activityDummyData, + date: new Date('2022-06-01'), + feeInAssetProfileCurrency: 0, + feeInBaseCurrency: 0, + quantity: 1, + SymbolProfile: { + ...symbolProfileDummyData, + currency: 'USD', + dataSource: 'YAHOO', + name: 'IBM', + symbol: 'IBM' + }, + type: 'DIVIDEND', + unitPriceInAssetProfileCurrency: 1.65 + }, + { + ...activityDummyData, + date: new Date('2023-06-01'), + feeInAssetProfileCurrency: 0, + feeInBaseCurrency: 0, + quantity: 1, + SymbolProfile: { + ...symbolProfileDummyData, + currency: 'USD', + dataSource: 'YAHOO', + name: 'IBM', + symbol: 'IBM' + }, + type: 'DIVIDEND', + unitPriceInAssetProfileCurrency: 1.65 + } + ]; + + const portfolioCalculator = portfolioCalculatorFactory.createCalculator({ + activities, + calculationType: PerformanceCalculationType.ROAI, + currency: 'USD', + userId: userDummyData.id + }); + + const portfolioSnapshot = await portfolioCalculator.computeSnapshot(); + + const msftPosition = portfolioSnapshot.positions.find( + ({ symbol }) => symbol === 'MSFT' + ); + const ibmPosition = portfolioSnapshot.positions.find( + ({ symbol }) => symbol === 'IBM' + ); + + expect(msftPosition.dividendInBaseCurrency).toEqual(new Big('1.92')); + expect(ibmPosition.dividendInBaseCurrency).toEqual(new Big('3.3')); + + const msftDividendLast12Months = new Big('1.3'); + const ibmDividendLast12Months = new Big('1.65'); + const totalInvestment = new Big('500'); + + expect(msftPosition.annualizedDividendYield).toBeCloseTo( + msftDividendLast12Months.div(new Big('300')).toNumber(), + 6 + ); + expect(ibmPosition.annualizedDividendYield).toBeCloseTo( + ibmDividendLast12Months.div(new Big('200')).toNumber(), + 6 + ); + + const expectedAnnualizedDividendYield = msftDividendLast12Months + .plus(ibmDividendLast12Months) + .div(totalInvestment) + .toNumber(); + + expect(portfolioSnapshot.annualizedDividendYield).toBeCloseTo( + expectedAnnualizedDividendYield, + 6 + ); + }); }); }); diff --git a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator.ts index 8f29c618e..a971d1c8e 100644 --- a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator.ts +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator.ts @@ -34,7 +34,7 @@ export class RoaiPortfolioCalculator extends PortfolioCalculator { let grossPerformanceWithCurrencyEffect = new Big(0); let hasErrors = false; let netPerformance = new Big(0); - let totalDividendsInBaseCurrency = new Big(0); + let totalDividendsLast12MonthsInBaseCurrency = new Big(0); let totalFeesWithCurrencyEffect = new Big(0); const totalInterestWithCurrencyEffect = new Big(0); let totalInvestment = new Big(0); @@ -47,10 +47,13 @@ export class RoaiPortfolioCalculator extends PortfolioCalculator { return includeInTotalAssetValue; } )) { - if (currentPosition.dividendInBaseCurrency) { - totalDividendsInBaseCurrency = totalDividendsInBaseCurrency.plus( - currentPosition.dividendInBaseCurrency - ); + if (currentPosition.investmentWithCurrencyEffect) { + totalDividendsLast12MonthsInBaseCurrency = + totalDividendsLast12MonthsInBaseCurrency.plus( + new Big(currentPosition.annualizedDividendYield ?? 0).mul( + currentPosition.investmentWithCurrencyEffect + ) + ); } if (currentPosition.feeInBaseCurrency) { @@ -114,7 +117,7 @@ export class RoaiPortfolioCalculator extends PortfolioCalculator { // Calculate annualized dividend yield for the entire portfolio const annualizedDividendYield = totalInvestmentWithCurrencyEffect.gt(0) - ? totalDividendsInBaseCurrency + ? totalDividendsLast12MonthsInBaseCurrency .div(totalInvestmentWithCurrencyEffect) .toNumber() : 0;