Feature/add annualized performance (#364)

* Add annualized performance

* Update changelog
pull/365/head
Thomas Kaul 3 years ago committed by GitHub
parent df2dfc20a1
commit 39f315aba0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## Unreleased
### Added
- Added the annualized performance to the portfolio summary tab on the home page
- Added the Ghostfolio Slack channel to the about page
## 1.51.0 - 11.09.2021

@ -0,0 +1,62 @@
import { PortfolioService } from './portfolio.service';
describe('PortfolioService', () => {
let portfolioService: PortfolioService;
beforeAll(async () => {
portfolioService = new PortfolioService(
null,
null,
null,
null,
null,
null,
null,
null,
null
);
});
it('Get annualized performance', async () => {
expect(
portfolioService.getAnnualizedPerformancePercent({
daysInMarket: NaN, // differenceInDays of date-fns returns NaN for the same day
netPerformancePercent: 0
})
).toEqual(0);
expect(
portfolioService.getAnnualizedPerformancePercent({
daysInMarket: 0,
netPerformancePercent: 0
})
).toEqual(0);
/**
* Source: https://www.readyratios.com/reference/analysis/annualized_rate.html
*/
expect(
portfolioService.getAnnualizedPerformancePercent({
daysInMarket: 65, // < 1 year
netPerformancePercent: 0.1025
})
).toBeCloseTo(0.729705);
expect(
portfolioService.getAnnualizedPerformancePercent({
daysInMarket: 365, // 1 year
netPerformancePercent: 0.05
})
).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: 0.2374
})
).toBeCloseTo(0.145);
});
});

@ -47,6 +47,7 @@ import {
} from '@prisma/client';
import Big from 'big.js';
import {
differenceInDays,
endOfToday,
format,
isAfter,
@ -58,7 +59,7 @@ import {
subDays,
subYears
} from 'date-fns';
import { isEmpty } from 'lodash';
import { isEmpty, isNumber } from 'lodash';
import {
HistoricalDataItem,
@ -80,6 +81,21 @@ export class PortfolioService {
private readonly symbolProfileService: SymbolProfileService
) {}
public getAnnualizedPerformancePercent({
daysInMarket,
netPerformancePercent
}: {
daysInMarket: number;
netPerformancePercent: number;
}) {
if (isNumber(daysInMarket) && daysInMarket > 0) {
const exponent = new Big(365).div(daysInMarket).toNumber();
return Math.pow(1 + netPerformancePercent, exponent) - 1;
}
return 0;
}
public async getInvestments(
aImpersonationId: string
): Promise<InvestmentItem[]> {
@ -715,6 +731,12 @@ export class PortfolioService {
const fees = this.getFees(orders);
const firstOrderDate = orders[0]?.date;
const annualizedPerformancePercent = this.getAnnualizedPerformancePercent({
daysInMarket: differenceInDays(new Date(), firstOrderDate),
netPerformancePercent:
performanceInformation.performance.currentNetPerformancePercent
});
const totalBuy = this.getTotalByType(orders, currency, TypeOfOrder.BUY);
const totalSell = this.getTotalByType(orders, currency, TypeOfOrder.SELL);
@ -726,6 +748,7 @@ export class PortfolioService {
return {
...performanceInformation.performance,
annualizedPerformancePercent,
fees,
firstOrderDate,
netWorth,

@ -146,7 +146,7 @@
<div class="col"><hr /></div>
</div>
<div class="row px-3 py-1">
<div class="d-flex flex-grow-1" i18n>Net Worth</div>
<div class="d-flex flex-grow-1 font-weight-bold" i18n>Net Worth</div>
<div class="d-flex justify-content-end">
<gf-value
class="justify-content-end"
@ -156,4 +156,17 @@
></gf-value>
</div>
</div>
<div class="row px-3 py-1">
<div class="d-flex flex-grow-1 ml-3" i18n>Annualized Performance</div>
<div class="d-flex flex-column flex-wrap justify-content-end">
<gf-value
class="justify-content-end"
position="end"
[colorizeSign]="true"
[isPercent]="true"
[locale]="locale"
[value]="isLoading ? undefined : summary?.annualizedPerformancePercent"
></gf-value>
</div>
</div>
</div>

@ -1,6 +1,7 @@
import { PortfolioPerformance } from './portfolio-performance.interface';
export interface PortfolioSummary extends PortfolioPerformance {
annualizedPerformancePercent: number;
cash: number;
committedFunds: number;
fees: number;

Loading…
Cancel
Save