Browse Source

Include dividend of holdings without average investment in dividend percentage

pull/7990/head
Thomas Kaul 1 week ago
parent
commit
c88592ec53
  1. 14
      apps/api/src/app/portfolio/calculator/portfolio-calculator.ts
  2. 12
      apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-googl-fee-and-dividend.spec.ts
  3. 38
      apps/api/src/app/portfolio/calculator/roai/portfolio-calculator.ts

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

@ -223,19 +223,15 @@ export abstract class PortfolioCalculator {
): PortfolioSnapshot;
protected abstract calculatePerformancePercentages({
accumulatedValuesByDate,
holdings
accumulatedValuesByDate
}: {
accumulatedValuesByDate: { [date: string]: AccumulatedValues };
holdings: PortfolioSnapshotHolding[];
}): { [date: string]: PerformancePercentages };
protected abstract calculatePerformancePercentagesForDateRange({
historicalDataItems,
holdings
historicalDataItems
}: {
historicalDataItems: HistoricalDataItem[];
holdings: PortfolioSnapshotHolding[];
}): { [date: string]: PerformancePercentages };
@LogPerformance
@ -680,8 +676,7 @@ export abstract class PortfolioCalculator {
});
const performancePercentagesByDate = this.calculatePerformancePercentages({
accumulatedValuesByDate,
holdings: positionsIncludedInHoldings
accumulatedValuesByDate
});
const historicalData: HistoricalDataItem[] = Object.entries(
@ -1443,8 +1438,7 @@ export abstract class PortfolioCalculator {
const performancePercentagesByDate =
this.calculatePerformancePercentagesForDateRange({
historicalDataItems: historicalDataItemsOfDateRange,
holdings: this.snapshot.positions
historicalDataItems: historicalDataItemsOfDateRange
});
const chart = historicalDataItemsOfDateRange.map((historicalDataItem) => {

12
apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-googl-fee-and-dividend.spec.ts

@ -234,8 +234,8 @@ describe('PortfolioCalculator', () => {
unitPriceInAssetProfileCurrency: 0.62
},
{
// The holding has a dividend, but no average investment. Thus the
// dividend percentage stays 0, like the dividend yield
// The holding has a dividend, but no average investment. Its dividend
// is part of the net performance, and thus of the dividend percentage
...activityDummyData,
assetProfile: {
...assetProfileDummyData,
@ -274,7 +274,7 @@ describe('PortfolioCalculator', () => {
expect(portfolioSnapshot.historicalData.at(-1)).toMatchObject({
dividendInBaseCurrency: 5.62,
dividendInPercentageWithCurrencyEffect: 0,
dividendInPercentageWithCurrencyEffect: 0.01882242615044544,
netPerformance: 19.87,
netPerformanceInPercentage: 0.06654832875611226
});
@ -282,7 +282,7 @@ describe('PortfolioCalculator', () => {
expect(performanceByDateRange).toMatchObject({
max: {
dividendInBaseCurrency: 5.62,
dividendInPercentageWithCurrencyEffect: 0,
dividendInPercentageWithCurrencyEffect: 0.01882242615044535,
netPerformance: 19.87,
netPerformanceInPercentage: 0.06654832875611194
}
@ -365,7 +365,7 @@ describe('PortfolioCalculator', () => {
expect(portfolioSnapshot.historicalData.at(-1)).toMatchObject({
dividendInBaseCurrency: 0.62,
dividendInPercentageWithCurrencyEffect: 0,
dividendInPercentageWithCurrencyEffect: 0.0020764954116149776,
netPerformance: 14.87,
netPerformanceInPercentage: 0.0498023980172818
});
@ -373,7 +373,7 @@ describe('PortfolioCalculator', () => {
expect(performanceByDateRange).toMatchObject({
max: {
dividendInBaseCurrency: 0.62,
dividendInPercentageWithCurrencyEffect: 0,
dividendInPercentageWithCurrencyEffect: 0.002076495411614967,
netPerformance: 14.87,
netPerformanceInPercentage: 0.049802398017281556
}

38
apps/api/src/app/portfolio/calculator/roai/portfolio-calculator.ts

@ -17,10 +17,7 @@ import {
AssetProfileIdentifier,
HistoricalDataItem
} from '@ghostfolio/common/interfaces';
import {
PortfolioSnapshot,
PortfolioSnapshotHolding
} from '@ghostfolio/common/models';
import { PortfolioSnapshot } from '@ghostfolio/common/models';
import { DateRange } from '@ghostfolio/common/types';
import { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type';
@ -168,15 +165,10 @@ export class RoaiPortfolioCalculator extends PortfolioCalculator {
}
protected calculatePerformancePercentages({
accumulatedValuesByDate,
holdings
accumulatedValuesByDate
}: {
accumulatedValuesByDate: { [date: string]: AccumulatedValues };
holdings: PortfolioSnapshotHolding[];
}): { [date: string]: PerformancePercentages } {
const hasDividendWithoutAverageInvestment =
this.hasDividendWithoutAverageInvestment(holdings);
const performancePercentagesByDate: {
[date: string]: PerformancePercentages;
} = {};
@ -193,7 +185,6 @@ export class RoaiPortfolioCalculator extends PortfolioCalculator {
] of Object.entries(accumulatedValuesByDate)) {
performancePercentagesByDate[date] = {
dividendInPercentageWithCurrencyEffect:
hasDividendWithoutAverageInvestment ||
totalAverageInvestmentValueWithCurrencyEffect.eq(0)
? 0
: totalDividendValueWithCurrencyEffect
@ -217,15 +208,10 @@ export class RoaiPortfolioCalculator extends PortfolioCalculator {
}
protected calculatePerformancePercentagesForDateRange({
historicalDataItems,
holdings
historicalDataItems
}: {
historicalDataItems: HistoricalDataItem[];
holdings: PortfolioSnapshotHolding[];
}): { [date: string]: PerformancePercentages } {
const hasDividendWithoutAverageInvestment =
this.hasDividendWithoutAverageInvestment(holdings);
const averageInvestmentValues: number[] = [];
const averageInvestmentValuesWithCurrencyEffect: number[] = [];
let grossPerformanceAtStartDate: number;
@ -276,7 +262,6 @@ export class RoaiPortfolioCalculator extends PortfolioCalculator {
performancePercentagesByDate[historicalDataItem.date] = {
dividendInPercentageWithCurrencyEffect:
!hasDividendWithoutAverageInvestment &&
averageInvestmentValueWithCurrencyEffect > 0
? historicalDataItem.dividendInBaseCurrency /
averageInvestmentValueWithCurrencyEffect
@ -770,21 +755,4 @@ export class RoaiPortfolioCalculator extends PortfolioCalculator {
protected getPerformanceCalculationType() {
return PerformanceCalculationType.ROAI;
}
private hasDividendWithoutAverageInvestment(
holdings: PortfolioSnapshotHolding[]
) {
// A holding with a dividend only (without a buy activity), and a holding
// without a market price, gives a dividend but no average investment.
// Such a holding makes the dividend percentage too high. Therefore the
// dividend percentage stays 0 in this case, like the dividend yield.
return holdings.some(
({ averageInvestmentWithCurrencyEffect, dividendInBaseCurrency }) => {
return (
!dividendInBaseCurrency.eq(0) &&
averageInvestmentWithCurrencyEffect.eq(0)
);
}
);
}
}

Loading…
Cancel
Save