Feature/refactor getAnnualizedPerformancePercent to portfolio calculator (#3226)

* Move getAnnualizedPerformancePercent() to portfolio calculator
pull/3037/head
Thomas Kaul 3 months ago committed by GitHub
parent b8533050b0
commit d7b579e3e8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -29,7 +29,7 @@ import {
max, max,
subDays subDays
} from 'date-fns'; } from 'date-fns';
import { isNumber, last, uniq } from 'lodash'; import { last, uniq } from 'lodash';
export abstract class PortfolioCalculator { export abstract class PortfolioCalculator {
protected static readonly ENABLE_LOGGING = false; protected static readonly ENABLE_LOGGING = false;
@ -80,23 +80,6 @@ export abstract class PortfolioCalculator {
positions: TimelinePosition[] positions: TimelinePosition[]
): CurrentPositions; ): CurrentPositions;
public getAnnualizedPerformancePercent({
daysInMarket,
netPerformancePercent
}: {
daysInMarket: number;
netPerformancePercent: Big;
}): Big {
if (isNumber(daysInMarket) && daysInMarket > 0) {
const exponent = new Big(365).div(daysInMarket).toNumber();
return new Big(
Math.pow(netPerformancePercent.plus(1).toNumber(), exponent)
).minus(1);
}
return new Big(0);
}
public async getChartData({ public async getChartData({
end = new Date(Date.now()), end = new Date(Date.now()),
start, start,

@ -1,12 +1,7 @@
import { import { PortfolioCalculatorFactory } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator.factory';
PerformanceCalculationType,
PortfolioCalculatorFactory
} from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator.factory';
import { CurrentRateService } from '@ghostfolio/api/app/portfolio/current-rate.service'; import { CurrentRateService } from '@ghostfolio/api/app/portfolio/current-rate.service';
import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service';
import { Big } from 'big.js';
describe('PortfolioCalculator', () => { describe('PortfolioCalculator', () => {
let currentRateService: CurrentRateService; let currentRateService: CurrentRateService;
let exchangeRateDataService: ExchangeRateDataService; let exchangeRateDataService: ExchangeRateDataService;
@ -28,64 +23,5 @@ describe('PortfolioCalculator', () => {
); );
}); });
describe('annualized performance percentage', () => { test.skip('Skip empty test', () => 1);
it('Get annualized performance', async () => {
const portfolioCalculator = factory.createCalculator({
activities: [],
calculationType: PerformanceCalculationType.TWR,
currency: 'CHF'
});
expect(
portfolioCalculator
.getAnnualizedPerformancePercent({
daysInMarket: NaN, // differenceInDays of date-fns returns NaN for the same day
netPerformancePercent: new Big(0)
})
.toNumber()
).toEqual(0);
expect(
portfolioCalculator
.getAnnualizedPerformancePercent({
daysInMarket: 0,
netPerformancePercent: new Big(0)
})
.toNumber()
).toEqual(0);
/**
* Source: https://www.readyratios.com/reference/analysis/annualized_rate.html
*/
expect(
portfolioCalculator
.getAnnualizedPerformancePercent({
daysInMarket: 65, // < 1 year
netPerformancePercent: new Big(0.1025)
})
.toNumber()
).toBeCloseTo(0.729705);
expect(
portfolioCalculator
.getAnnualizedPerformancePercent({
daysInMarket: 365, // 1 year
netPerformancePercent: new Big(0.05)
})
.toNumber()
).toBeCloseTo(0.05);
/**
* Source: https://www.investopedia.com/terms/a/annualized-total-return.asp#annualized-return-formula-and-calculation
*/
expect(
portfolioCalculator
.getAnnualizedPerformancePercent({
daysInMarket: 575, // > 1 year
netPerformancePercent: new Big(0.2374)
})
.toNumber()
).toBeCloseTo(0.145);
});
});
}); });

@ -0,0 +1,78 @@
import { Big } from 'big.js';
import { PortfolioService } from './portfolio.service';
describe('PortfolioService', () => {
let portfolioService: PortfolioService;
beforeAll(async () => {
portfolioService = new PortfolioService(
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null
);
});
describe('annualized performance percentage', () => {
it('Get annualized performance', async () => {
expect(
portfolioService
.getAnnualizedPerformancePercent({
daysInMarket: NaN, // differenceInDays of date-fns returns NaN for the same day
netPerformancePercent: new Big(0)
})
.toNumber()
).toEqual(0);
expect(
portfolioService
.getAnnualizedPerformancePercent({
daysInMarket: 0,
netPerformancePercent: new Big(0)
})
.toNumber()
).toEqual(0);
/**
* Source: https://www.readyratios.com/reference/analysis/annualized_rate.html
*/
expect(
portfolioService
.getAnnualizedPerformancePercent({
daysInMarket: 65, // < 1 year
netPerformancePercent: new Big(0.1025)
})
.toNumber()
).toBeCloseTo(0.729705);
expect(
portfolioService
.getAnnualizedPerformancePercent({
daysInMarket: 365, // 1 year
netPerformancePercent: new Big(0.05)
})
.toNumber()
).toBeCloseTo(0.05);
/**
* Source: https://www.investopedia.com/terms/a/annualized-total-return.asp#annualized-return-formula-and-calculation
*/
expect(
portfolioService
.getAnnualizedPerformancePercent({
daysInMarket: 575, // > 1 year
netPerformancePercent: new Big(0.2374)
})
.toNumber()
).toBeCloseTo(0.145);
});
});
});

@ -78,7 +78,7 @@ import {
parseISO, parseISO,
set set
} from 'date-fns'; } from 'date-fns';
import { isEmpty, last, uniq, uniqBy } from 'lodash'; import { isEmpty, isNumber, last, uniq, uniqBy } from 'lodash';
import { PortfolioCalculator } from './calculator/portfolio-calculator'; import { PortfolioCalculator } from './calculator/portfolio-calculator';
import { import {
@ -217,6 +217,24 @@ export class PortfolioService {
}; };
} }
public getAnnualizedPerformancePercent({
daysInMarket,
netPerformancePercent
}: {
daysInMarket: number;
netPerformancePercent: Big;
}): Big {
if (isNumber(daysInMarket) && daysInMarket > 0) {
const exponent = new Big(365).div(daysInMarket).toNumber();
return new Big(
Math.pow(netPerformancePercent.plus(1).toNumber(), exponent)
).minus(1);
}
return new Big(0);
}
public async getDividends({ public async getDividends({
activities, activities,
groupBy groupBy
@ -1769,34 +1787,20 @@ export class PortfolioService {
const daysInMarket = differenceInDays(new Date(), firstOrderDate); const daysInMarket = differenceInDays(new Date(), firstOrderDate);
const annualizedPerformancePercent = this.calculatorFactory const annualizedPerformancePercent = this.getAnnualizedPerformancePercent({
.createCalculator({
activities: [],
calculationType: PerformanceCalculationType.TWR,
currency: userCurrency
})
.getAnnualizedPerformancePercent({
daysInMarket, daysInMarket,
netPerformancePercent: new Big( netPerformancePercent: new Big(
performanceInformation.performance.currentNetPerformancePercent performanceInformation.performance.currentNetPerformancePercent
) )
}) })?.toNumber();
?.toNumber();
const annualizedPerformancePercentWithCurrencyEffect = const annualizedPerformancePercentWithCurrencyEffect =
this.calculatorFactory this.getAnnualizedPerformancePercent({
.createCalculator({
activities: [],
calculationType: PerformanceCalculationType.TWR,
currency: userCurrency
})
.getAnnualizedPerformancePercent({
daysInMarket, daysInMarket,
netPerformancePercent: new Big( netPerformancePercent: new Big(
performanceInformation.performance.currentNetPerformancePercentWithCurrencyEffect performanceInformation.performance.currentNetPerformancePercentWithCurrencyEffect
) )
}) })?.toNumber();
?.toNumber();
return { return {
...performanceInformation.performance, ...performanceInformation.performance,

Loading…
Cancel
Save