Browse Source

Feature/accentuate all time high and low (#428)

* Accentuate all time high and all time low

* Update changelog

Co-authored-by: Valentin Zickner <ghostfolio@zickner.ch>
pull/487/head
Thomas Kaul 3 years ago
committed by GitHub
parent
commit
1c2ca5b96b
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 6
      CHANGELOG.md
  2. 6
      apps/api/src/app/portfolio/interfaces/portfolio-position-detail.interface.ts
  3. 8
      apps/api/src/app/portfolio/interfaces/timeline-info.interface.ts
  4. 27
      apps/api/src/app/portfolio/portfolio-calculator.spec.ts
  5. 72
      apps/api/src/app/portfolio/portfolio-calculator.ts
  6. 18
      apps/api/src/app/portfolio/portfolio.controller.ts
  7. 61
      apps/api/src/app/portfolio/portfolio.service.ts
  8. 10
      apps/client/src/app/components/portfolio-performance/portfolio-performance.component.html
  9. 2
      apps/client/src/app/components/portfolio-performance/portfolio-performance.component.ts
  10. 6
      apps/client/src/app/pages/home/home-page.component.ts
  11. 2
      apps/client/src/app/pages/home/home-page.html
  12. 6
      apps/client/src/app/pages/zen/zen-page.component.ts
  13. 2
      apps/client/src/app/pages/zen/zen-page.html
  14. 16
      apps/client/src/app/services/data.service.ts
  15. 2
      libs/common/src/lib/interfaces/index.ts
  16. 7
      libs/common/src/lib/interfaces/portfolio-chart.interface.ts
  17. 2
      libs/common/src/lib/interfaces/portfolio-performance.interface.ts

6
CHANGELOG.md

@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## Unreleased
### Added
- Accentuated the all time high and the all time low
## 1.79.0 - 21.11.2021 ## 1.79.0 - 21.11.2021
### Added ### Added

6
apps/api/src/app/portfolio/interfaces/portfolio-position-detail.interface.ts

@ -21,6 +21,12 @@ export interface PortfolioPositionDetail {
transactionCount: number; transactionCount: number;
} }
export interface HistoricalDataContainer {
isAllTimeHigh: boolean;
isAllTimeLow: boolean;
items: HistoricalDataItem[];
}
export interface HistoricalDataItem { export interface HistoricalDataItem {
averagePrice?: number; averagePrice?: number;
date: string; date: string;

8
apps/api/src/app/portfolio/interfaces/timeline-info.interface.ts

@ -0,0 +1,8 @@
import { TimelinePeriod } from '@ghostfolio/api/app/portfolio/interfaces/timeline-period.interface';
import Big from 'big.js';
export interface TimelineInfoInterface {
maxNetPerformance: Big;
minNetPerformance: Big;
timelinePeriods: TimelinePeriod[];
}

27
apps/api/src/app/portfolio/portfolio-calculator.spec.ts

@ -1502,11 +1502,11 @@ describe('PortfolioCalculator', () => {
accuracy: 'year' accuracy: 'year'
} }
]; ];
const timeline: TimelinePeriod[] = const timelineInfo = await portfolioCalculator.calculateTimeline(
await portfolioCalculator.calculateTimeline(
timelineSpecification, timelineSpecification,
'2021-06-30' '2021-06-30'
); );
const timeline: TimelinePeriod[] = timelineInfo.timelinePeriods;
expect(timeline).toEqual([ expect(timeline).toEqual([
{ {
@ -1622,11 +1622,11 @@ describe('PortfolioCalculator', () => {
accuracy: 'year' accuracy: 'year'
} }
]; ];
const timeline: TimelinePeriod[] = const timelineInfo = await portfolioCalculator.calculateTimeline(
await portfolioCalculator.calculateTimeline(
timelineSpecification, timelineSpecification,
'2021-06-30' '2021-06-30'
); );
const timeline: TimelinePeriod[] = timelineInfo.timelinePeriods;
expect(timeline).toEqual([ expect(timeline).toEqual([
{ {
@ -1665,11 +1665,11 @@ describe('PortfolioCalculator', () => {
accuracy: 'month' accuracy: 'month'
} }
]; ];
const timeline: TimelinePeriod[] = const timelineInfo = await portfolioCalculator.calculateTimeline(
await portfolioCalculator.calculateTimeline(
timelineSpecification, timelineSpecification,
'2021-06-30' '2021-06-30'
); );
const timeline: TimelinePeriod[] = timelineInfo.timelinePeriods;
expect(timeline).toEqual([ expect(timeline).toEqual([
{ {
@ -1883,6 +1883,9 @@ describe('PortfolioCalculator', () => {
value: new Big('3186.9') // 15 * (144.38 + days=851 * 0.08) value: new Big('3186.9') // 15 * (144.38 + days=851 * 0.08)
} }
]); ]);
expect(timelineInfo.maxNetPerformance).toEqual(new Big('547.9'));
expect(timelineInfo.minNetPerformance).toEqual(new Big('0'));
}); });
it('with yearly and monthly mixed', async () => { it('with yearly and monthly mixed', async () => {
@ -1901,11 +1904,11 @@ describe('PortfolioCalculator', () => {
accuracy: 'month' accuracy: 'month'
} }
]; ];
const timeline: TimelinePeriod[] = const timelineInfo = await portfolioCalculator.calculateTimeline(
await portfolioCalculator.calculateTimeline(
timelineSpecification, timelineSpecification,
'2021-06-30' '2021-06-30'
); );
const timeline: TimelinePeriod[] = timelineInfo.timelinePeriods;
expect(timeline).toEqual([ expect(timeline).toEqual([
{ {
@ -1987,11 +1990,11 @@ describe('PortfolioCalculator', () => {
accuracy: 'day' accuracy: 'day'
} }
]; ];
const timeline: TimelinePeriod[] = const timelineInfo = await portfolioCalculator.calculateTimeline(
await portfolioCalculator.calculateTimeline(
timelineSpecification, timelineSpecification,
'2021-06-30' '2021-06-30'
); );
const timeline: TimelinePeriod[] = timelineInfo.timelinePeriods;
expect(timeline).toEqual( expect(timeline).toEqual(
expect.objectContaining([ expect.objectContaining([
@ -2296,11 +2299,11 @@ describe('PortfolioCalculator', () => {
accuracy: 'year' accuracy: 'year'
} }
]; ];
const timeline: TimelinePeriod[] = const timelineInfo = await portfolioCalculator.calculateTimeline(
await portfolioCalculator.calculateTimeline(
timelineSpecification, timelineSpecification,
'2020-01-01' '2020-01-01'
); );
const timeline: TimelinePeriod[] = timelineInfo.timelinePeriods;
expect(timeline).toEqual([ expect(timeline).toEqual([
{ {

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

@ -1,3 +1,4 @@
import { TimelineInfoInterface } from '@ghostfolio/api/app/portfolio/interfaces/timeline-info.interface';
import { OrderType } from '@ghostfolio/api/models/order-type'; import { OrderType } from '@ghostfolio/api/models/order-type';
import { IDataGatheringItem } from '@ghostfolio/api/services/interfaces/interfaces'; import { IDataGatheringItem } from '@ghostfolio/api/services/interfaces/interfaces';
import { DATE_FORMAT, parseDate, resetHours } from '@ghostfolio/common/helper'; import { DATE_FORMAT, parseDate, resetHours } from '@ghostfolio/common/helper';
@ -365,16 +366,20 @@ export class PortfolioCalculator {
public async calculateTimeline( public async calculateTimeline(
timelineSpecification: TimelineSpecification[], timelineSpecification: TimelineSpecification[],
endDate: string endDate: string
): Promise<TimelinePeriod[]> { ): Promise<TimelineInfoInterface> {
if (timelineSpecification.length === 0) { if (timelineSpecification.length === 0) {
return []; return {
maxNetPerformance: new Big(0),
minNetPerformance: new Big(0),
timelinePeriods: []
};
} }
const startDate = timelineSpecification[0].start; const startDate = timelineSpecification[0].start;
const start = parseDate(startDate); const start = parseDate(startDate);
const end = parseDate(endDate); const end = parseDate(endDate);
const timelinePeriodPromises: Promise<TimelinePeriod[]>[] = []; const timelinePeriodPromises: Promise<TimelineInfoInterface>[] = [];
let i = 0; let i = 0;
let j = -1; let j = -1;
for ( for (
@ -417,11 +422,40 @@ export class PortfolioCalculator {
} }
} }
const timelinePeriods: TimelinePeriod[][] = await Promise.all( const timelineInfoInterfaces: TimelineInfoInterface[] = await Promise.all(
timelinePeriodPromises timelinePeriodPromises
); );
const minNetPerformance = timelineInfoInterfaces
.map((timelineInfo) => timelineInfo.minNetPerformance)
.filter((performance) => performance !== null)
.reduce((minPerformance, current) => {
if (minPerformance.lt(current)) {
return minPerformance;
} else {
return current;
}
});
const maxNetPerformance = timelineInfoInterfaces
.map((timelineInfo) => timelineInfo.maxNetPerformance)
.filter((performance) => performance !== null)
.reduce((maxPerformance, current) => {
if (maxPerformance.gt(current)) {
return maxPerformance;
} else {
return current;
}
});
const timelinePeriods = timelineInfoInterfaces.map(
(timelineInfo) => timelineInfo.timelinePeriods
);
return flatten(timelinePeriods); return {
maxNetPerformance,
minNetPerformance,
timelinePeriods: flatten(timelinePeriods)
};
} }
private calculateOverallPerformance( private calculateOverallPerformance(
@ -513,7 +547,7 @@ export class PortfolioCalculator {
j: number, j: number,
startDate: Date, startDate: Date,
endDate: Date endDate: Date
): Promise<TimelinePeriod[]> { ): Promise<TimelineInfoInterface> {
let investment: Big = new Big(0); let investment: Big = new Big(0);
let fees: Big = new Big(0); let fees: Big = new Big(0);
@ -569,6 +603,8 @@ export class PortfolioCalculator {
} }
const results: TimelinePeriod[] = []; const results: TimelinePeriod[] = [];
let maxNetPerformance: Big = null;
let minNetPerformance: Big = null;
for ( for (
let currentDate = startDate; let currentDate = startDate;
isBefore(currentDate, endDate); isBefore(currentDate, endDate);
@ -592,18 +628,36 @@ export class PortfolioCalculator {
} }
if (!invalid) { if (!invalid) {
const grossPerformance = value.minus(investment); const grossPerformance = value.minus(investment);
const netPerformance = grossPerformance.minus(fees);
if (
minNetPerformance === null ||
minNetPerformance.gt(netPerformance)
) {
minNetPerformance = netPerformance;
}
if (
maxNetPerformance === null ||
maxNetPerformance.lt(netPerformance)
) {
maxNetPerformance = netPerformance;
}
const result = { const result = {
grossPerformance, grossPerformance,
investment, investment,
netPerformance,
value, value,
date: currentDateAsString, date: currentDateAsString
netPerformance: grossPerformance.minus(fees)
}; };
results.push(result); results.push(result);
} }
} }
return results; return {
maxNetPerformance,
minNetPerformance,
timelinePeriods: results
};
} }
private getFactor(type: OrderType) { private getFactor(type: OrderType) {

18
apps/api/src/app/portfolio/portfolio.controller.ts

@ -8,6 +8,7 @@ import { ConfigurationService } from '@ghostfolio/api/services/configuration.ser
import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data.service'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data.service';
import { baseCurrency } from '@ghostfolio/common/config'; import { baseCurrency } from '@ghostfolio/common/config';
import { import {
PortfolioChart,
PortfolioDetails, PortfolioDetails,
PortfolioPerformance, PortfolioPerformance,
PortfolioPublicDetails, PortfolioPublicDetails,
@ -32,10 +33,7 @@ import { AuthGuard } from '@nestjs/passport';
import { Response } from 'express'; import { Response } from 'express';
import { StatusCodes, getReasonPhrase } from 'http-status-codes'; import { StatusCodes, getReasonPhrase } from 'http-status-codes';
import { import { PortfolioPositionDetail } from './interfaces/portfolio-position-detail.interface';
HistoricalDataItem,
PortfolioPositionDetail
} from './interfaces/portfolio-position-detail.interface';
import { PortfolioPositions } from './interfaces/portfolio-positions.interface'; import { PortfolioPositions } from './interfaces/portfolio-positions.interface';
import { PortfolioService } from './portfolio.service'; import { PortfolioService } from './portfolio.service';
@ -92,12 +90,14 @@ export class PortfolioController {
@Headers('impersonation-id') impersonationId, @Headers('impersonation-id') impersonationId,
@Query('range') range, @Query('range') range,
@Res() res: Response @Res() res: Response
): Promise<HistoricalDataItem[]> { ): Promise<PortfolioChart> {
let chartData = await this.portfolioService.getChart( const historicalDataContainer = await this.portfolioService.getChart(
impersonationId, impersonationId,
range range
); );
let chartData = historicalDataContainer.items;
let hasNullValue = false; let hasNullValue = false;
chartData.forEach((chartDataItem) => { chartData.forEach((chartDataItem) => {
@ -130,7 +130,11 @@ export class PortfolioController {
}); });
} }
return <any>res.json(chartData); return <any>res.json({
chart: chartData,
isAllTimeHigh: historicalDataContainer.isAllTimeHigh,
isAllTimeLow: historicalDataContainer.isAllTimeLow
});
} }
@Get('details') @Get('details')

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

@ -56,12 +56,14 @@ import {
parse, parse,
parseISO, parseISO,
setDayOfYear, setDayOfYear,
startOfDay,
subDays, subDays,
subYears subYears
} from 'date-fns'; } from 'date-fns';
import { isEmpty } from 'lodash'; import { isEmpty } from 'lodash';
import { import {
HistoricalDataContainer,
HistoricalDataItem, HistoricalDataItem,
PortfolioPositionDetail PortfolioPositionDetail
} from './interfaces/portfolio-position-detail.interface'; } from './interfaces/portfolio-position-detail.interface';
@ -164,7 +166,7 @@ export class PortfolioService {
public async getChart( public async getChart(
aImpersonationId: string, aImpersonationId: string,
aDateRange: DateRange = 'max' aDateRange: DateRange = 'max'
): Promise<HistoricalDataItem[]> { ): Promise<HistoricalDataContainer> {
const userId = await this.getUserId(aImpersonationId, this.request.user.id); const userId = await this.getUserId(aImpersonationId, this.request.user.id);
const portfolioCalculator = new PortfolioCalculator( const portfolioCalculator = new PortfolioCalculator(
@ -175,14 +177,21 @@ export class PortfolioService {
const { transactionPoints } = await this.getTransactionPoints({ userId }); const { transactionPoints } = await this.getTransactionPoints({ userId });
portfolioCalculator.setTransactionPoints(transactionPoints); portfolioCalculator.setTransactionPoints(transactionPoints);
if (transactionPoints.length === 0) { if (transactionPoints.length === 0) {
return []; return {
isAllTimeHigh: false,
isAllTimeLow: false,
items: []
};
} }
let portfolioStart = parse( let portfolioStart = parse(
transactionPoints[0].date, transactionPoints[0].date,
DATE_FORMAT, DATE_FORMAT,
new Date() new Date()
); );
portfolioStart = this.getStartDate(aDateRange, portfolioStart);
// Get start date for the full portfolio because of because of the
// min and max calculation
portfolioStart = this.getStartDate('max', portfolioStart);
const timelineSpecification: TimelineSpecification[] = [ const timelineSpecification: TimelineSpecification[] = [
{ {
@ -191,18 +200,52 @@ export class PortfolioService {
} }
]; ];
const timeline = await portfolioCalculator.calculateTimeline( const timelineInfo = await portfolioCalculator.calculateTimeline(
timelineSpecification, timelineSpecification,
format(new Date(), DATE_FORMAT) format(new Date(), DATE_FORMAT)
); );
return timeline const timeline = timelineInfo.timelinePeriods;
const items = timeline
.filter((timelineItem) => timelineItem !== null) .filter((timelineItem) => timelineItem !== null)
.map((timelineItem) => ({ .map((timelineItem) => ({
date: timelineItem.date, date: timelineItem.date,
marketPrice: timelineItem.value, marketPrice: timelineItem.value,
value: timelineItem.netPerformance.toNumber() value: timelineItem.netPerformance.toNumber()
})); }));
let lastItem = null;
if (timeline.length > 0) {
lastItem = timeline[timeline.length - 1];
}
let isAllTimeHigh = timelineInfo.maxNetPerformance?.eq(
lastItem?.netPerformance
);
let isAllTimeLow = timelineInfo.minNetPerformance?.eq(
lastItem?.netPerformance
);
if (isAllTimeHigh && isAllTimeLow) {
isAllTimeHigh = false;
isAllTimeLow = false;
}
portfolioStart = startOfDay(
this.getStartDate(
aDateRange,
parse(transactionPoints[0].date, DATE_FORMAT, new Date())
)
);
return {
isAllTimeHigh,
isAllTimeLow,
items: items.filter((item) => {
// Filter items of date range
return !isAfter(portfolioStart, parseDate(item.date));
})
};
} }
public async getDetails( public async getDetails(
@ -639,7 +682,9 @@ export class PortfolioService {
currentGrossPerformancePercent: 0, currentGrossPerformancePercent: 0,
currentNetPerformance: 0, currentNetPerformance: 0,
currentNetPerformancePercent: 0, currentNetPerformancePercent: 0,
currentValue: 0 currentValue: 0,
isAllTimeHigh: false,
isAllTimeLow: false
} }
}; };
} }
@ -672,7 +717,9 @@ export class PortfolioService {
currentGrossPerformancePercent, currentGrossPerformancePercent,
currentNetPerformance, currentNetPerformance,
currentNetPerformancePercent, currentNetPerformancePercent,
currentValue currentValue,
isAllTimeHigh: true, // TODO
isAllTimeLow: false // TODO
} }
}; };
} }

10
apps/client/src/app/components/portfolio-performance/portfolio-performance.component.html

@ -1,5 +1,11 @@
<div class="container p-0"> <div class="container p-0">
<div class="row no-gutters"> <div
class="no-gutters row"
[ngClass]="{
'text-danger': isAllTimeLow,
'text-success': isAllTimeHigh
}"
>
<div class="flex-grow-1"></div> <div class="flex-grow-1"></div>
<div *ngIf="isLoading" class="align-items-center d-flex"> <div *ngIf="isLoading" class="align-items-center d-flex">
<ngx-skeleton-loader <ngx-skeleton-loader
@ -12,8 +18,8 @@
></ngx-skeleton-loader> ></ngx-skeleton-loader>
</div> </div>
<div <div
[hidden]="isLoading"
class="display-4 font-weight-bold m-0 text-center value-container" class="display-4 font-weight-bold m-0 text-center value-container"
[hidden]="isLoading"
> >
<span #value id="value"></span> <span #value id="value"></span>
</div> </div>

2
apps/client/src/app/components/portfolio-performance/portfolio-performance.component.ts

@ -19,6 +19,8 @@ import { isNumber } from 'lodash';
}) })
export class PortfolioPerformanceComponent implements OnChanges, OnInit { export class PortfolioPerformanceComponent implements OnChanges, OnInit {
@Input() baseCurrency: string; @Input() baseCurrency: string;
@Input() isAllTimeHigh: boolean;
@Input() isAllTimeLow: boolean;
@Input() isLoading: boolean; @Input() isLoading: boolean;
@Input() locale: string; @Input() locale: string;
@Input() performance: PortfolioPerformance; @Input() performance: PortfolioPerformance;

6
apps/client/src/app/pages/home/home-page.component.ts

@ -59,6 +59,8 @@ export class HomePageComponent implements OnDestroy, OnInit {
public hasPermissionToAccessFearAndGreedIndex: boolean; public hasPermissionToAccessFearAndGreedIndex: boolean;
public hasPermissionToCreateOrder: boolean; public hasPermissionToCreateOrder: boolean;
public historicalDataItems: LineChartItem[]; public historicalDataItems: LineChartItem[];
public isAllTimeHigh: boolean;
public isAllTimeLow: boolean;
public isLoadingPerformance = true; public isLoadingPerformance = true;
public isLoadingSummary = true; public isLoadingSummary = true;
public performance: PortfolioPerformance; public performance: PortfolioPerformance;
@ -166,12 +168,14 @@ export class HomePageComponent implements OnDestroy, OnInit {
.fetchChart({ range: this.dateRange }) .fetchChart({ range: this.dateRange })
.pipe(takeUntil(this.unsubscribeSubject)) .pipe(takeUntil(this.unsubscribeSubject))
.subscribe((chartData) => { .subscribe((chartData) => {
this.historicalDataItems = chartData.map((chartDataItem) => { this.historicalDataItems = chartData.chart.map((chartDataItem) => {
return { return {
date: chartDataItem.date, date: chartDataItem.date,
value: chartDataItem.value value: chartDataItem.value
}; };
}); });
this.isAllTimeHigh = chartData.isAllTimeHigh;
this.isAllTimeLow = chartData.isAllTimeLow;
this.changeDetectorRef.markForCheck(); this.changeDetectorRef.markForCheck();
}); });

2
apps/client/src/app/pages/home/home-page.html

@ -54,6 +54,8 @@
<gf-portfolio-performance <gf-portfolio-performance
class="pb-4" class="pb-4"
[baseCurrency]="user?.settings?.baseCurrency" [baseCurrency]="user?.settings?.baseCurrency"
[isAllTimeHigh]="isAllTimeHigh"
[isAllTimeLow]="isAllTimeLow"
[isLoading]="isLoadingPerformance" [isLoading]="isLoadingPerformance"
[locale]="user?.settings?.locale" [locale]="user?.settings?.locale"
[performance]="performance" [performance]="performance"

6
apps/client/src/app/pages/zen/zen-page.component.ts

@ -39,6 +39,8 @@ export class ZenPageComponent implements AfterViewInit, OnDestroy, OnInit {
public hasImpersonationId: boolean; public hasImpersonationId: boolean;
public hasPermissionToCreateOrder: boolean; public hasPermissionToCreateOrder: boolean;
public historicalDataItems: LineChartItem[]; public historicalDataItems: LineChartItem[];
public isAllTimeHigh: boolean;
public isAllTimeLow: boolean;
public isLoadingPerformance = true; public isLoadingPerformance = true;
public performance: PortfolioPerformance; public performance: PortfolioPerformance;
public positions: Position[]; public positions: Position[];
@ -114,12 +116,14 @@ export class ZenPageComponent implements AfterViewInit, OnDestroy, OnInit {
.fetchChart({ range: this.dateRange }) .fetchChart({ range: this.dateRange })
.pipe(takeUntil(this.unsubscribeSubject)) .pipe(takeUntil(this.unsubscribeSubject))
.subscribe((chartData) => { .subscribe((chartData) => {
this.historicalDataItems = chartData.map((chartDataItem) => { this.historicalDataItems = chartData.chart.map((chartDataItem) => {
return { return {
date: chartDataItem.date, date: chartDataItem.date,
value: chartDataItem.value value: chartDataItem.value
}; };
}); });
this.isAllTimeHigh = chartData.isAllTimeHigh;
this.isAllTimeLow = chartData.isAllTimeLow;
this.changeDetectorRef.markForCheck(); this.changeDetectorRef.markForCheck();
}); });

2
apps/client/src/app/pages/zen/zen-page.html

@ -47,6 +47,8 @@
<gf-portfolio-performance <gf-portfolio-performance
class="pb-4" class="pb-4"
[baseCurrency]="user?.settings?.baseCurrency" [baseCurrency]="user?.settings?.baseCurrency"
[isAllTimeHigh]="isAllTimeHigh"
[isAllTimeLow]="isAllTimeLow"
[isLoading]="isLoadingPerformance" [isLoading]="isLoadingPerformance"
[locale]="user?.settings?.locale" [locale]="user?.settings?.locale"
[performance]="performance" [performance]="performance"

16
apps/client/src/app/services/data.service.ts

@ -5,10 +5,7 @@ import { CreateAccountDto } from '@ghostfolio/api/app/account/create-account.dto
import { UpdateAccountDto } from '@ghostfolio/api/app/account/update-account.dto'; import { UpdateAccountDto } from '@ghostfolio/api/app/account/update-account.dto';
import { CreateOrderDto } from '@ghostfolio/api/app/order/create-order.dto'; import { CreateOrderDto } from '@ghostfolio/api/app/order/create-order.dto';
import { UpdateOrderDto } from '@ghostfolio/api/app/order/update-order.dto'; import { UpdateOrderDto } from '@ghostfolio/api/app/order/update-order.dto';
import { import { PortfolioPositionDetail } from '@ghostfolio/api/app/portfolio/interfaces/portfolio-position-detail.interface';
HistoricalDataItem,
PortfolioPositionDetail
} from '@ghostfolio/api/app/portfolio/interfaces/portfolio-position-detail.interface';
import { PortfolioPositions } from '@ghostfolio/api/app/portfolio/interfaces/portfolio-positions.interface'; import { PortfolioPositions } from '@ghostfolio/api/app/portfolio/interfaces/portfolio-positions.interface';
import { LookupItem } from '@ghostfolio/api/app/symbol/interfaces/lookup-item.interface'; import { LookupItem } from '@ghostfolio/api/app/symbol/interfaces/lookup-item.interface';
import { SymbolItem } from '@ghostfolio/api/app/symbol/interfaces/symbol-item.interface'; import { SymbolItem } from '@ghostfolio/api/app/symbol/interfaces/symbol-item.interface';
@ -21,6 +18,7 @@ import {
AdminData, AdminData,
Export, Export,
InfoItem, InfoItem,
PortfolioChart,
PortfolioDetails, PortfolioDetails,
PortfolioPerformance, PortfolioPerformance,
PortfolioPublicDetails, PortfolioPublicDetails,
@ -30,12 +28,8 @@ import {
} from '@ghostfolio/common/interfaces'; } from '@ghostfolio/common/interfaces';
import { InvestmentItem } from '@ghostfolio/common/interfaces/investment-item.interface'; import { InvestmentItem } from '@ghostfolio/common/interfaces/investment-item.interface';
import { permissions } from '@ghostfolio/common/permissions'; import { permissions } from '@ghostfolio/common/permissions';
import { AccountWithValue, DateRange } from '@ghostfolio/common/types'; import { DateRange } from '@ghostfolio/common/types';
import { import { DataSource, Order as OrderModel } from '@prisma/client';
Account as AccountModel,
DataSource,
Order as OrderModel
} from '@prisma/client';
import { parseISO } from 'date-fns'; import { parseISO } from 'date-fns';
import { cloneDeep } from 'lodash'; import { cloneDeep } from 'lodash';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
@ -91,7 +85,7 @@ export class DataService {
} }
public fetchChart({ range }: { range: DateRange }) { public fetchChart({ range }: { range: DateRange }) {
return this.http.get<HistoricalDataItem[]>('/api/portfolio/chart', { return this.http.get<PortfolioChart>('/api/portfolio/chart', {
params: { range } params: { range }
}); });
} }

2
libs/common/src/lib/interfaces/index.ts

@ -3,6 +3,7 @@ import { Accounts } from './accounts.interface';
import { AdminData } from './admin-data.interface'; import { AdminData } from './admin-data.interface';
import { Export } from './export.interface'; import { Export } from './export.interface';
import { InfoItem } from './info-item.interface'; import { InfoItem } from './info-item.interface';
import { PortfolioChart } from './portfolio-chart.interface';
import { PortfolioDetails } from './portfolio-details.interface'; import { PortfolioDetails } from './portfolio-details.interface';
import { PortfolioItem } from './portfolio-item.interface'; import { PortfolioItem } from './portfolio-item.interface';
import { PortfolioOverview } from './portfolio-overview.interface'; import { PortfolioOverview } from './portfolio-overview.interface';
@ -24,6 +25,7 @@ export {
AdminData, AdminData,
Export, Export,
InfoItem, InfoItem,
PortfolioChart,
PortfolioDetails, PortfolioDetails,
PortfolioItem, PortfolioItem,
PortfolioOverview, PortfolioOverview,

7
libs/common/src/lib/interfaces/portfolio-chart.interface.ts

@ -0,0 +1,7 @@
import { HistoricalDataItem } from '@ghostfolio/api/app/portfolio/interfaces/portfolio-position-detail.interface';
export interface PortfolioChart {
isAllTimeHigh: boolean;
isAllTimeLow: boolean;
chart: HistoricalDataItem[];
}

2
libs/common/src/lib/interfaces/portfolio-performance.interface.ts

@ -5,4 +5,6 @@ export interface PortfolioPerformance {
currentNetPerformance: number; currentNetPerformance: number;
currentNetPerformancePercent: number; currentNetPerformancePercent: number;
currentValue: number; currentValue: number;
isAllTimeHigh: boolean;
isAllTimeLow: boolean;
} }

Loading…
Cancel
Save