Browse Source

Add annualized performance

pull/364/head
Thomas 4 years ago
parent
commit
557c280707
  1. 45
      apps/api/src/app/portfolio/portfolio.service.spec.ts
  2. 25
      apps/api/src/app/portfolio/portfolio.service.ts
  3. 15
      apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html
  4. 1
      libs/common/src/lib/interfaces/portfolio-summary.interface.ts

45
apps/api/src/app/portfolio/portfolio.service.spec.ts

@ -0,0 +1,45 @@
import { PortfolioService } from './portfolio.service';
describe('PortfolioService', () => {
let portfolioService: PortfolioService;
beforeAll(async () => {
portfolioService = new PortfolioService(
null,
null,
null,
null,
null,
null,
null,
null,
null
);
});
/**
* Source: https://www.investopedia.com/terms/a/annualized-total-return.asp#annualized-return-formula-and-calculation
*/
fit('Get annualized performance', async () => {
expect(
portfolioService.getAnnualizedPerformancePercent({
daysInMarket: NaN,
netPerformancePercent: 0
})
).toEqual(0);
expect(
portfolioService.getAnnualizedPerformancePercent({
daysInMarket: 0,
netPerformancePercent: 0
})
).toEqual(0);
expect(
portfolioService.getAnnualizedPerformancePercent({
daysInMarket: 575,
netPerformancePercent: 0.2374
})
).toEqual(0.1447846830315136);
});
});

25
apps/api/src/app/portfolio/portfolio.service.ts

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

15
apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html

@ -146,7 +146,7 @@
<div class="col"><hr /></div> <div class="col"><hr /></div>
</div> </div>
<div class="row px-3 py-1"> <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"> <div class="d-flex justify-content-end">
<gf-value <gf-value
class="justify-content-end" class="justify-content-end"
@ -156,4 +156,17 @@
></gf-value> ></gf-value>
</div> </div>
</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> </div>

1
libs/common/src/lib/interfaces/portfolio-summary.interface.ts

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

Loading…
Cancel
Save