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>
This commit is contained in:
parent
572bfc59b8
commit
1c2ca5b96b
@ -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/),
|
||||
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
|
||||
|
||||
### Added
|
||||
|
@ -21,6 +21,12 @@ export interface PortfolioPositionDetail {
|
||||
transactionCount: number;
|
||||
}
|
||||
|
||||
export interface HistoricalDataContainer {
|
||||
isAllTimeHigh: boolean;
|
||||
isAllTimeLow: boolean;
|
||||
items: HistoricalDataItem[];
|
||||
}
|
||||
|
||||
export interface HistoricalDataItem {
|
||||
averagePrice?: number;
|
||||
date: string;
|
||||
|
@ -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[];
|
||||
}
|
@ -1502,11 +1502,11 @@ describe('PortfolioCalculator', () => {
|
||||
accuracy: 'year'
|
||||
}
|
||||
];
|
||||
const timeline: TimelinePeriod[] =
|
||||
await portfolioCalculator.calculateTimeline(
|
||||
timelineSpecification,
|
||||
'2021-06-30'
|
||||
);
|
||||
const timelineInfo = await portfolioCalculator.calculateTimeline(
|
||||
timelineSpecification,
|
||||
'2021-06-30'
|
||||
);
|
||||
const timeline: TimelinePeriod[] = timelineInfo.timelinePeriods;
|
||||
|
||||
expect(timeline).toEqual([
|
||||
{
|
||||
@ -1622,11 +1622,11 @@ describe('PortfolioCalculator', () => {
|
||||
accuracy: 'year'
|
||||
}
|
||||
];
|
||||
const timeline: TimelinePeriod[] =
|
||||
await portfolioCalculator.calculateTimeline(
|
||||
timelineSpecification,
|
||||
'2021-06-30'
|
||||
);
|
||||
const timelineInfo = await portfolioCalculator.calculateTimeline(
|
||||
timelineSpecification,
|
||||
'2021-06-30'
|
||||
);
|
||||
const timeline: TimelinePeriod[] = timelineInfo.timelinePeriods;
|
||||
|
||||
expect(timeline).toEqual([
|
||||
{
|
||||
@ -1665,11 +1665,11 @@ describe('PortfolioCalculator', () => {
|
||||
accuracy: 'month'
|
||||
}
|
||||
];
|
||||
const timeline: TimelinePeriod[] =
|
||||
await portfolioCalculator.calculateTimeline(
|
||||
timelineSpecification,
|
||||
'2021-06-30'
|
||||
);
|
||||
const timelineInfo = await portfolioCalculator.calculateTimeline(
|
||||
timelineSpecification,
|
||||
'2021-06-30'
|
||||
);
|
||||
const timeline: TimelinePeriod[] = timelineInfo.timelinePeriods;
|
||||
|
||||
expect(timeline).toEqual([
|
||||
{
|
||||
@ -1883,6 +1883,9 @@ describe('PortfolioCalculator', () => {
|
||||
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 () => {
|
||||
@ -1901,11 +1904,11 @@ describe('PortfolioCalculator', () => {
|
||||
accuracy: 'month'
|
||||
}
|
||||
];
|
||||
const timeline: TimelinePeriod[] =
|
||||
await portfolioCalculator.calculateTimeline(
|
||||
timelineSpecification,
|
||||
'2021-06-30'
|
||||
);
|
||||
const timelineInfo = await portfolioCalculator.calculateTimeline(
|
||||
timelineSpecification,
|
||||
'2021-06-30'
|
||||
);
|
||||
const timeline: TimelinePeriod[] = timelineInfo.timelinePeriods;
|
||||
|
||||
expect(timeline).toEqual([
|
||||
{
|
||||
@ -1987,11 +1990,11 @@ describe('PortfolioCalculator', () => {
|
||||
accuracy: 'day'
|
||||
}
|
||||
];
|
||||
const timeline: TimelinePeriod[] =
|
||||
await portfolioCalculator.calculateTimeline(
|
||||
timelineSpecification,
|
||||
'2021-06-30'
|
||||
);
|
||||
const timelineInfo = await portfolioCalculator.calculateTimeline(
|
||||
timelineSpecification,
|
||||
'2021-06-30'
|
||||
);
|
||||
const timeline: TimelinePeriod[] = timelineInfo.timelinePeriods;
|
||||
|
||||
expect(timeline).toEqual(
|
||||
expect.objectContaining([
|
||||
@ -2296,11 +2299,11 @@ describe('PortfolioCalculator', () => {
|
||||
accuracy: 'year'
|
||||
}
|
||||
];
|
||||
const timeline: TimelinePeriod[] =
|
||||
await portfolioCalculator.calculateTimeline(
|
||||
timelineSpecification,
|
||||
'2020-01-01'
|
||||
);
|
||||
const timelineInfo = await portfolioCalculator.calculateTimeline(
|
||||
timelineSpecification,
|
||||
'2020-01-01'
|
||||
);
|
||||
const timeline: TimelinePeriod[] = timelineInfo.timelinePeriods;
|
||||
|
||||
expect(timeline).toEqual([
|
||||
{
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { TimelineInfoInterface } from '@ghostfolio/api/app/portfolio/interfaces/timeline-info.interface';
|
||||
import { OrderType } from '@ghostfolio/api/models/order-type';
|
||||
import { IDataGatheringItem } from '@ghostfolio/api/services/interfaces/interfaces';
|
||||
import { DATE_FORMAT, parseDate, resetHours } from '@ghostfolio/common/helper';
|
||||
@ -365,16 +366,20 @@ export class PortfolioCalculator {
|
||||
public async calculateTimeline(
|
||||
timelineSpecification: TimelineSpecification[],
|
||||
endDate: string
|
||||
): Promise<TimelinePeriod[]> {
|
||||
): Promise<TimelineInfoInterface> {
|
||||
if (timelineSpecification.length === 0) {
|
||||
return [];
|
||||
return {
|
||||
maxNetPerformance: new Big(0),
|
||||
minNetPerformance: new Big(0),
|
||||
timelinePeriods: []
|
||||
};
|
||||
}
|
||||
|
||||
const startDate = timelineSpecification[0].start;
|
||||
const start = parseDate(startDate);
|
||||
const end = parseDate(endDate);
|
||||
|
||||
const timelinePeriodPromises: Promise<TimelinePeriod[]>[] = [];
|
||||
const timelinePeriodPromises: Promise<TimelineInfoInterface>[] = [];
|
||||
let i = 0;
|
||||
let j = -1;
|
||||
for (
|
||||
@ -417,11 +422,40 @@ export class PortfolioCalculator {
|
||||
}
|
||||
}
|
||||
|
||||
const timelinePeriods: TimelinePeriod[][] = await Promise.all(
|
||||
const timelineInfoInterfaces: TimelineInfoInterface[] = await Promise.all(
|
||||
timelinePeriodPromises
|
||||
);
|
||||
const minNetPerformance = timelineInfoInterfaces
|
||||
.map((timelineInfo) => timelineInfo.minNetPerformance)
|
||||
.filter((performance) => performance !== null)
|
||||
.reduce((minPerformance, current) => {
|
||||
if (minPerformance.lt(current)) {
|
||||
return minPerformance;
|
||||
} else {
|
||||
return current;
|
||||
}
|
||||
});
|
||||
|
||||
return flatten(timelinePeriods);
|
||||
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 {
|
||||
maxNetPerformance,
|
||||
minNetPerformance,
|
||||
timelinePeriods: flatten(timelinePeriods)
|
||||
};
|
||||
}
|
||||
|
||||
private calculateOverallPerformance(
|
||||
@ -513,7 +547,7 @@ export class PortfolioCalculator {
|
||||
j: number,
|
||||
startDate: Date,
|
||||
endDate: Date
|
||||
): Promise<TimelinePeriod[]> {
|
||||
): Promise<TimelineInfoInterface> {
|
||||
let investment: Big = new Big(0);
|
||||
let fees: Big = new Big(0);
|
||||
|
||||
@ -569,6 +603,8 @@ export class PortfolioCalculator {
|
||||
}
|
||||
|
||||
const results: TimelinePeriod[] = [];
|
||||
let maxNetPerformance: Big = null;
|
||||
let minNetPerformance: Big = null;
|
||||
for (
|
||||
let currentDate = startDate;
|
||||
isBefore(currentDate, endDate);
|
||||
@ -592,18 +628,36 @@ export class PortfolioCalculator {
|
||||
}
|
||||
if (!invalid) {
|
||||
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 = {
|
||||
grossPerformance,
|
||||
investment,
|
||||
netPerformance,
|
||||
value,
|
||||
date: currentDateAsString,
|
||||
netPerformance: grossPerformance.minus(fees)
|
||||
date: currentDateAsString
|
||||
};
|
||||
results.push(result);
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
return {
|
||||
maxNetPerformance,
|
||||
minNetPerformance,
|
||||
timelinePeriods: results
|
||||
};
|
||||
}
|
||||
|
||||
private getFactor(type: OrderType) {
|
||||
|
@ -8,6 +8,7 @@ import { ConfigurationService } from '@ghostfolio/api/services/configuration.ser
|
||||
import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data.service';
|
||||
import { baseCurrency } from '@ghostfolio/common/config';
|
||||
import {
|
||||
PortfolioChart,
|
||||
PortfolioDetails,
|
||||
PortfolioPerformance,
|
||||
PortfolioPublicDetails,
|
||||
@ -32,10 +33,7 @@ import { AuthGuard } from '@nestjs/passport';
|
||||
import { Response } from 'express';
|
||||
import { StatusCodes, getReasonPhrase } from 'http-status-codes';
|
||||
|
||||
import {
|
||||
HistoricalDataItem,
|
||||
PortfolioPositionDetail
|
||||
} from './interfaces/portfolio-position-detail.interface';
|
||||
import { PortfolioPositionDetail } from './interfaces/portfolio-position-detail.interface';
|
||||
import { PortfolioPositions } from './interfaces/portfolio-positions.interface';
|
||||
import { PortfolioService } from './portfolio.service';
|
||||
|
||||
@ -92,12 +90,14 @@ export class PortfolioController {
|
||||
@Headers('impersonation-id') impersonationId,
|
||||
@Query('range') range,
|
||||
@Res() res: Response
|
||||
): Promise<HistoricalDataItem[]> {
|
||||
let chartData = await this.portfolioService.getChart(
|
||||
): Promise<PortfolioChart> {
|
||||
const historicalDataContainer = await this.portfolioService.getChart(
|
||||
impersonationId,
|
||||
range
|
||||
);
|
||||
|
||||
let chartData = historicalDataContainer.items;
|
||||
|
||||
let hasNullValue = false;
|
||||
|
||||
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')
|
||||
|
@ -56,12 +56,14 @@ import {
|
||||
parse,
|
||||
parseISO,
|
||||
setDayOfYear,
|
||||
startOfDay,
|
||||
subDays,
|
||||
subYears
|
||||
} from 'date-fns';
|
||||
import { isEmpty } from 'lodash';
|
||||
|
||||
import {
|
||||
HistoricalDataContainer,
|
||||
HistoricalDataItem,
|
||||
PortfolioPositionDetail
|
||||
} from './interfaces/portfolio-position-detail.interface';
|
||||
@ -164,7 +166,7 @@ export class PortfolioService {
|
||||
public async getChart(
|
||||
aImpersonationId: string,
|
||||
aDateRange: DateRange = 'max'
|
||||
): Promise<HistoricalDataItem[]> {
|
||||
): Promise<HistoricalDataContainer> {
|
||||
const userId = await this.getUserId(aImpersonationId, this.request.user.id);
|
||||
|
||||
const portfolioCalculator = new PortfolioCalculator(
|
||||
@ -175,14 +177,21 @@ export class PortfolioService {
|
||||
const { transactionPoints } = await this.getTransactionPoints({ userId });
|
||||
portfolioCalculator.setTransactionPoints(transactionPoints);
|
||||
if (transactionPoints.length === 0) {
|
||||
return [];
|
||||
return {
|
||||
isAllTimeHigh: false,
|
||||
isAllTimeLow: false,
|
||||
items: []
|
||||
};
|
||||
}
|
||||
let portfolioStart = parse(
|
||||
transactionPoints[0].date,
|
||||
DATE_FORMAT,
|
||||
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[] = [
|
||||
{
|
||||
@ -191,18 +200,52 @@ export class PortfolioService {
|
||||
}
|
||||
];
|
||||
|
||||
const timeline = await portfolioCalculator.calculateTimeline(
|
||||
const timelineInfo = await portfolioCalculator.calculateTimeline(
|
||||
timelineSpecification,
|
||||
format(new Date(), DATE_FORMAT)
|
||||
);
|
||||
|
||||
return timeline
|
||||
const timeline = timelineInfo.timelinePeriods;
|
||||
|
||||
const items = timeline
|
||||
.filter((timelineItem) => timelineItem !== null)
|
||||
.map((timelineItem) => ({
|
||||
date: timelineItem.date,
|
||||
marketPrice: timelineItem.value,
|
||||
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(
|
||||
@ -639,7 +682,9 @@ export class PortfolioService {
|
||||
currentGrossPerformancePercent: 0,
|
||||
currentNetPerformance: 0,
|
||||
currentNetPerformancePercent: 0,
|
||||
currentValue: 0
|
||||
currentValue: 0,
|
||||
isAllTimeHigh: false,
|
||||
isAllTimeLow: false
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -672,7 +717,9 @@ export class PortfolioService {
|
||||
currentGrossPerformancePercent,
|
||||
currentNetPerformance,
|
||||
currentNetPerformancePercent,
|
||||
currentValue
|
||||
currentValue,
|
||||
isAllTimeHigh: true, // TODO
|
||||
isAllTimeLow: false // TODO
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -1,5 +1,11 @@
|
||||
<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 *ngIf="isLoading" class="align-items-center d-flex">
|
||||
<ngx-skeleton-loader
|
||||
@ -12,8 +18,8 @@
|
||||
></ngx-skeleton-loader>
|
||||
</div>
|
||||
<div
|
||||
[hidden]="isLoading"
|
||||
class="display-4 font-weight-bold m-0 text-center value-container"
|
||||
[hidden]="isLoading"
|
||||
>
|
||||
<span #value id="value"></span>
|
||||
</div>
|
||||
|
@ -19,6 +19,8 @@ import { isNumber } from 'lodash';
|
||||
})
|
||||
export class PortfolioPerformanceComponent implements OnChanges, OnInit {
|
||||
@Input() baseCurrency: string;
|
||||
@Input() isAllTimeHigh: boolean;
|
||||
@Input() isAllTimeLow: boolean;
|
||||
@Input() isLoading: boolean;
|
||||
@Input() locale: string;
|
||||
@Input() performance: PortfolioPerformance;
|
||||
|
@ -59,6 +59,8 @@ export class HomePageComponent implements OnDestroy, OnInit {
|
||||
public hasPermissionToAccessFearAndGreedIndex: boolean;
|
||||
public hasPermissionToCreateOrder: boolean;
|
||||
public historicalDataItems: LineChartItem[];
|
||||
public isAllTimeHigh: boolean;
|
||||
public isAllTimeLow: boolean;
|
||||
public isLoadingPerformance = true;
|
||||
public isLoadingSummary = true;
|
||||
public performance: PortfolioPerformance;
|
||||
@ -166,12 +168,14 @@ export class HomePageComponent implements OnDestroy, OnInit {
|
||||
.fetchChart({ range: this.dateRange })
|
||||
.pipe(takeUntil(this.unsubscribeSubject))
|
||||
.subscribe((chartData) => {
|
||||
this.historicalDataItems = chartData.map((chartDataItem) => {
|
||||
this.historicalDataItems = chartData.chart.map((chartDataItem) => {
|
||||
return {
|
||||
date: chartDataItem.date,
|
||||
value: chartDataItem.value
|
||||
};
|
||||
});
|
||||
this.isAllTimeHigh = chartData.isAllTimeHigh;
|
||||
this.isAllTimeLow = chartData.isAllTimeLow;
|
||||
|
||||
this.changeDetectorRef.markForCheck();
|
||||
});
|
||||
|
@ -54,6 +54,8 @@
|
||||
<gf-portfolio-performance
|
||||
class="pb-4"
|
||||
[baseCurrency]="user?.settings?.baseCurrency"
|
||||
[isAllTimeHigh]="isAllTimeHigh"
|
||||
[isAllTimeLow]="isAllTimeLow"
|
||||
[isLoading]="isLoadingPerformance"
|
||||
[locale]="user?.settings?.locale"
|
||||
[performance]="performance"
|
||||
|
@ -39,6 +39,8 @@ export class ZenPageComponent implements AfterViewInit, OnDestroy, OnInit {
|
||||
public hasImpersonationId: boolean;
|
||||
public hasPermissionToCreateOrder: boolean;
|
||||
public historicalDataItems: LineChartItem[];
|
||||
public isAllTimeHigh: boolean;
|
||||
public isAllTimeLow: boolean;
|
||||
public isLoadingPerformance = true;
|
||||
public performance: PortfolioPerformance;
|
||||
public positions: Position[];
|
||||
@ -114,12 +116,14 @@ export class ZenPageComponent implements AfterViewInit, OnDestroy, OnInit {
|
||||
.fetchChart({ range: this.dateRange })
|
||||
.pipe(takeUntil(this.unsubscribeSubject))
|
||||
.subscribe((chartData) => {
|
||||
this.historicalDataItems = chartData.map((chartDataItem) => {
|
||||
this.historicalDataItems = chartData.chart.map((chartDataItem) => {
|
||||
return {
|
||||
date: chartDataItem.date,
|
||||
value: chartDataItem.value
|
||||
};
|
||||
});
|
||||
this.isAllTimeHigh = chartData.isAllTimeHigh;
|
||||
this.isAllTimeLow = chartData.isAllTimeLow;
|
||||
|
||||
this.changeDetectorRef.markForCheck();
|
||||
});
|
||||
|
@ -47,6 +47,8 @@
|
||||
<gf-portfolio-performance
|
||||
class="pb-4"
|
||||
[baseCurrency]="user?.settings?.baseCurrency"
|
||||
[isAllTimeHigh]="isAllTimeHigh"
|
||||
[isAllTimeLow]="isAllTimeLow"
|
||||
[isLoading]="isLoadingPerformance"
|
||||
[locale]="user?.settings?.locale"
|
||||
[performance]="performance"
|
||||
|
@ -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 { CreateOrderDto } from '@ghostfolio/api/app/order/create-order.dto';
|
||||
import { UpdateOrderDto } from '@ghostfolio/api/app/order/update-order.dto';
|
||||
import {
|
||||
HistoricalDataItem,
|
||||
PortfolioPositionDetail
|
||||
} from '@ghostfolio/api/app/portfolio/interfaces/portfolio-position-detail.interface';
|
||||
import { PortfolioPositionDetail } from '@ghostfolio/api/app/portfolio/interfaces/portfolio-position-detail.interface';
|
||||
import { PortfolioPositions } from '@ghostfolio/api/app/portfolio/interfaces/portfolio-positions.interface';
|
||||
import { LookupItem } from '@ghostfolio/api/app/symbol/interfaces/lookup-item.interface';
|
||||
import { SymbolItem } from '@ghostfolio/api/app/symbol/interfaces/symbol-item.interface';
|
||||
@ -21,6 +18,7 @@ import {
|
||||
AdminData,
|
||||
Export,
|
||||
InfoItem,
|
||||
PortfolioChart,
|
||||
PortfolioDetails,
|
||||
PortfolioPerformance,
|
||||
PortfolioPublicDetails,
|
||||
@ -30,12 +28,8 @@ import {
|
||||
} from '@ghostfolio/common/interfaces';
|
||||
import { InvestmentItem } from '@ghostfolio/common/interfaces/investment-item.interface';
|
||||
import { permissions } from '@ghostfolio/common/permissions';
|
||||
import { AccountWithValue, DateRange } from '@ghostfolio/common/types';
|
||||
import {
|
||||
Account as AccountModel,
|
||||
DataSource,
|
||||
Order as OrderModel
|
||||
} from '@prisma/client';
|
||||
import { DateRange } from '@ghostfolio/common/types';
|
||||
import { DataSource, Order as OrderModel } from '@prisma/client';
|
||||
import { parseISO } from 'date-fns';
|
||||
import { cloneDeep } from 'lodash';
|
||||
import { Observable } from 'rxjs';
|
||||
@ -91,7 +85,7 @@ export class DataService {
|
||||
}
|
||||
|
||||
public fetchChart({ range }: { range: DateRange }) {
|
||||
return this.http.get<HistoricalDataItem[]>('/api/portfolio/chart', {
|
||||
return this.http.get<PortfolioChart>('/api/portfolio/chart', {
|
||||
params: { range }
|
||||
});
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ import { Accounts } from './accounts.interface';
|
||||
import { AdminData } from './admin-data.interface';
|
||||
import { Export } from './export.interface';
|
||||
import { InfoItem } from './info-item.interface';
|
||||
import { PortfolioChart } from './portfolio-chart.interface';
|
||||
import { PortfolioDetails } from './portfolio-details.interface';
|
||||
import { PortfolioItem } from './portfolio-item.interface';
|
||||
import { PortfolioOverview } from './portfolio-overview.interface';
|
||||
@ -24,6 +25,7 @@ export {
|
||||
AdminData,
|
||||
Export,
|
||||
InfoItem,
|
||||
PortfolioChart,
|
||||
PortfolioDetails,
|
||||
PortfolioItem,
|
||||
PortfolioOverview,
|
||||
|
@ -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[];
|
||||
}
|
@ -5,4 +5,6 @@ export interface PortfolioPerformance {
|
||||
currentNetPerformance: number;
|
||||
currentNetPerformancePercent: number;
|
||||
currentValue: number;
|
||||
isAllTimeHigh: boolean;
|
||||
isAllTimeLow: boolean;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user