diff --git a/apps/api/src/app/core/portfolio-calculator.ts b/apps/api/src/app/core/portfolio-calculator.ts index a92479e7f..a0d32f147 100644 --- a/apps/api/src/app/core/portfolio-calculator.ts +++ b/apps/api/src/app/core/portfolio-calculator.ts @@ -3,10 +3,7 @@ import { GetValueObject } from '@ghostfolio/api/app/core/current-rate.service'; import { OrderType } from '@ghostfolio/api/models/order-type'; -import { - MarketState, - Type -} from '@ghostfolio/api/services/interfaces/interfaces'; +import { resetHours } from '@ghostfolio/common/helper'; import { TimelinePosition } from '@ghostfolio/common/interfaces'; import { Currency } from '@prisma/client'; import Big from 'big.js'; @@ -24,7 +21,6 @@ import { subDays } from 'date-fns'; import { flatten } from 'lodash'; -import { resetHours } from '@ghostfolio/common/helper'; const DATE_FORMAT = 'yyyy-MM-dd'; @@ -145,19 +141,15 @@ export class PortfolioCalculator { averagePrice: item.investment.div(item.quantity), currency: item.currency, firstBuyDate: item.firstBuyDate, - marketState: MarketState.open, // TODO - quantity: item.quantity, - symbol: item.symbol, - investment: item.investment, - marketPrice: marketValue?.marketPrice, - transactionCount: item.transactionCount, grossPerformance, grossPerformancePercentage: marketValue ? grossPerformance.div(item.investment) : null, - url: '', // TODO - name: '', // TODO, - type: Type.Unknown // TODO + investment: item.investment, + marketPrice: marketValue?.marketPrice, + quantity: item.quantity, + symbol: item.symbol, + transactionCount: item.transactionCount }; } diff --git a/apps/api/src/app/portfolio/interfaces/portfolio-positions.interface.ts b/apps/api/src/app/portfolio/interfaces/portfolio-positions.interface.ts index 238f1718c..fa6141a7a 100644 --- a/apps/api/src/app/portfolio/interfaces/portfolio-positions.interface.ts +++ b/apps/api/src/app/portfolio/interfaces/portfolio-positions.interface.ts @@ -1,5 +1,5 @@ -import { TimelinePosition } from '@ghostfolio/common/interfaces'; +import { Position } from '@ghostfolio/common/interfaces'; export interface PortfolioPositions { - positions: TimelinePosition[]; + positions: Position[]; } diff --git a/apps/api/src/app/portfolio/portfolio.controller.ts b/apps/api/src/app/portfolio/portfolio.controller.ts index fef612fa5..d63ba8119 100644 --- a/apps/api/src/app/portfolio/portfolio.controller.ts +++ b/apps/api/src/app/portfolio/portfolio.controller.ts @@ -283,9 +283,13 @@ export class PortfolioController { @Get('positions') @UseGuards(AuthGuard('jwt')) public async getPositions( - @Headers('impersonation-id') impersonationId + @Headers('impersonation-id') impersonationId, + @Query('range') range ): Promise { - const positions = await this.portfolioService.getPositions(impersonationId); + const positions = await this.portfolioService.getPositions( + impersonationId, + range + ); return { positions }; } diff --git a/apps/api/src/app/portfolio/portfolio.service.ts b/apps/api/src/app/portfolio/portfolio.service.ts index 9c5e97d18..1a8bfd5d7 100644 --- a/apps/api/src/app/portfolio/portfolio.service.ts +++ b/apps/api/src/app/portfolio/portfolio.service.ts @@ -14,10 +14,15 @@ import { DataProviderService } from '@ghostfolio/api/services/data-provider.serv import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data.service'; import { ImpersonationService } from '@ghostfolio/api/services/impersonation.service'; import { IOrder } from '@ghostfolio/api/services/interfaces/interfaces'; +import { + MarketState, + Type +} from '@ghostfolio/api/services/interfaces/interfaces'; import { RulesService } from '@ghostfolio/api/services/rules.service'; import { PortfolioItem, - PortfolioOverview + PortfolioOverview, + Position } from '@ghostfolio/common/interfaces'; import { DateRange, RequestWithUser } from '@ghostfolio/common/types'; import { Inject, Injectable } from '@nestjs/common'; @@ -192,7 +197,10 @@ export class PortfolioService { })); } - public async getPositions(aImpersonationId: string) { + public async getPositions( + aImpersonationId: string, + aDateRange: DateRange = 'max' + ): Promise { const impersonationUserId = await this.impersonationService.validateImpersonationId( aImpersonationId, @@ -210,13 +218,23 @@ export class PortfolioService { portfolioCalculator.setTransactionPoints(transactionPoints); + // TODO: get positions for date range + console.log('Date range:', aDateRange); const positions = await portfolioCalculator.getCurrentPositions(); return Object.values(positions).map((position) => { return { ...position, - grossPerformance: Number(position.grossPerformance), - grossPerformancePercentage: Number(position.grossPerformancePercentage) + averagePrice: new Big(position.averagePrice).toNumber(), + grossPerformance: new Big(position.grossPerformance).toNumber(), + grossPerformancePercentage: new Big( + position.grossPerformancePercentage + ).toNumber(), + investment: new Big(position.investment).toNumber(), + name: '', // TODO + quantity: new Big(position.quantity).toNumber(), + type: Type.Unknown, // TODO + url: '' // TODO }; }); } diff --git a/apps/api/src/models/portfolio.ts b/apps/api/src/models/portfolio.ts index 38ea79e41..b961333b5 100644 --- a/apps/api/src/models/portfolio.ts +++ b/apps/api/src/models/portfolio.ts @@ -80,6 +80,7 @@ export class Portfolio implements PortfolioInterface { this.getSymbols().forEach((symbol) => { positions[symbol] = { + symbol, averagePrice: portfolioItemsYesterday?.positions[symbol]?.averagePrice, currency: portfolioItemsYesterday?.positions[symbol]?.currency, firstBuyDate: portfolioItemsYesterday?.positions[symbol]?.firstBuyDate, @@ -723,6 +724,7 @@ export class Portfolio implements PortfolioInterface { const positions: { [symbol: string]: Position } = {}; this.getSymbols().forEach((symbol) => { positions[symbol] = { + symbol, averagePrice: 0, currency: undefined, firstBuyDate: null, @@ -759,12 +761,13 @@ export class Portfolio implements PortfolioInterface { const yesterday = getYesterday(); - let positions: { [symbol: string]: Position } = {}; + const positions: { [symbol: string]: Position } = {}; if (isAfter(yesterday, this.getMinDate())) { // Add yesterday this.getSymbols().forEach((symbol) => { positions[symbol] = { + symbol, averagePrice: 0, currency: undefined, firstBuyDate: null, @@ -773,6 +776,7 @@ export class Portfolio implements PortfolioInterface { marketPrice: historicalData[symbol]?.[format(yesterday, 'yyyy-MM-dd')] ?.marketPrice || 0, + name: '', quantity: 0, transactionCount: 0 }; diff --git a/apps/client/src/app/components/position/position.component.ts b/apps/client/src/app/components/position/position.component.ts index 840a1240a..5e191df72 100644 --- a/apps/client/src/app/components/position/position.component.ts +++ b/apps/client/src/app/components/position/position.component.ts @@ -8,7 +8,7 @@ import { import { MatDialog } from '@angular/material/dialog'; import { ActivatedRoute, Router } from '@angular/router'; import { UNKNOWN_KEY } from '@ghostfolio/common/config'; -import { TimelinePosition } from '@ghostfolio/common/interfaces'; +import { Position } from '@ghostfolio/common/interfaces'; import { Subject } from 'rxjs'; import { takeUntil } from 'rxjs/operators'; @@ -25,7 +25,7 @@ export class PositionComponent implements OnDestroy, OnInit { @Input() deviceType: string; @Input() isLoading: boolean; @Input() locale: string; - @Input() position: TimelinePosition; + @Input() position: Position; @Input() range: string; public unknownKey = UNKNOWN_KEY; diff --git a/apps/client/src/app/components/positions/positions.component.ts b/apps/client/src/app/components/positions/positions.component.ts index 0c57f9bf7..6d12ac133 100644 --- a/apps/client/src/app/components/positions/positions.component.ts +++ b/apps/client/src/app/components/positions/positions.component.ts @@ -9,7 +9,7 @@ import { MarketState, Type } from '@ghostfolio/api/services/interfaces/interfaces'; -import { TimelinePosition } from '@ghostfolio/common/interfaces'; +import { Position } from '@ghostfolio/common/interfaces'; @Component({ selector: 'gf-positions', @@ -21,12 +21,12 @@ export class PositionsComponent implements OnChanges, OnInit { @Input() baseCurrency: string; @Input() deviceType: string; @Input() locale: string; - @Input() positions: TimelinePosition[]; + @Input() positions: Position[]; @Input() range: string; public hasPositions: boolean; - public positionsRest: TimelinePosition[] = []; - public positionsWithPriority: TimelinePosition[] = []; + public positionsRest: Position[] = []; + public positionsWithPriority: Position[] = []; private ignoreTypes = [Type.Cash]; diff --git a/apps/client/src/app/pages/home/home-page.component.ts b/apps/client/src/app/pages/home/home-page.component.ts index 169723dbf..115370e45 100644 --- a/apps/client/src/app/pages/home/home-page.component.ts +++ b/apps/client/src/app/pages/home/home-page.component.ts @@ -24,7 +24,7 @@ import { UserService } from '@ghostfolio/client/services/user/user.service'; import { PortfolioOverview, PortfolioPerformance, - TimelinePosition, + Position, User } from '@ghostfolio/common/interfaces'; import { hasPermission, permissions } from '@ghostfolio/common/permissions'; @@ -65,7 +65,7 @@ export class HomePageComponent implements AfterViewInit, OnDestroy, OnInit { public isLoadingPerformance = true; public overview: PortfolioOverview; public performance: PortfolioPerformance; - public positions: TimelinePosition[]; + public positions: Position[]; public routeQueryParams: Subscription; public user: User; @@ -231,14 +231,11 @@ export class HomePageComponent implements AfterViewInit, OnDestroy, OnInit { }); this.dataService - .fetchPositions(/* { range: this.dateRange } */) // TODO + .fetchPositions({ range: this.dateRange }) .pipe(takeUntil(this.unsubscribeSubject)) .subscribe((response) => { - console.log(response); - this.positions = response.positions; - this.hasPositions = - this.positions && Object.keys(this.positions).length > 1; + this.hasPositions = this.positions?.length > 0; this.changeDetectorRef.markForCheck(); }); diff --git a/apps/client/src/app/pages/transactions/transactions-page.component.ts b/apps/client/src/app/pages/transactions/transactions-page.component.ts index 15231a402..39eefdd32 100644 --- a/apps/client/src/app/pages/transactions/transactions-page.component.ts +++ b/apps/client/src/app/pages/transactions/transactions-page.component.ts @@ -107,7 +107,6 @@ export class TransactionsPageComponent implements OnDestroy, OnInit { }); this.fetchOrders(); - this.fetchPositions(); } public fetchOrders() { @@ -125,15 +124,6 @@ export class TransactionsPageComponent implements OnDestroy, OnInit { }); } - public fetchPositions() { - this.dataService - .fetchPositions() - .pipe(takeUntil(this.unsubscribeSubject)) - .subscribe((response) => { - console.log(response); - }); - } - public onCloneTransaction(aTransaction: OrderModel) { this.openCreateTransactionDialog(aTransaction); } diff --git a/apps/client/src/app/services/data.service.ts b/apps/client/src/app/services/data.service.ts index e4b9194c9..91b5741e2 100644 --- a/apps/client/src/app/services/data.service.ts +++ b/apps/client/src/app/services/data.service.ts @@ -27,6 +27,7 @@ import { User } from '@ghostfolio/common/interfaces'; import { permissions } from '@ghostfolio/common/permissions'; +import { DateRange } from '@ghostfolio/common/types'; import { Order as OrderModel } from '@prisma/client'; import { Account as AccountModel } from '@prisma/client'; import { parseISO } from 'date-fns'; @@ -84,9 +85,9 @@ export class DataService { return this.http.get('/api/access'); } - public fetchChart(aParams: { [param: string]: any }) { + public fetchChart({ range }: { range: DateRange }) { return this.http.get('/api/portfolio/chart', { - params: aParams + params: { range } }); } @@ -110,12 +111,14 @@ export class DataService { return this.http.get(`/api/symbol/${aSymbol}`); } - public fetchPositions(): Observable { - return this.http.get('/api/portfolio/positions').pipe( - map((respose) => { - return respose; - }) - ); + public fetchPositions({ + range + }: { + range: DateRange; + }): Observable { + return this.http.get('/api/portfolio/positions', { + params: { range } + }); } public fetchSymbols(aQuery: string) { diff --git a/libs/common/src/lib/interfaces/position.interface.ts b/libs/common/src/lib/interfaces/position.interface.ts index 4094a0e79..e7c64df82 100644 --- a/libs/common/src/lib/interfaces/position.interface.ts +++ b/libs/common/src/lib/interfaces/position.interface.ts @@ -1,12 +1,23 @@ +import { + MarketState, + Type +} from '@ghostfolio/api/services/interfaces/interfaces'; import { Currency } from '@prisma/client'; export interface Position { averagePrice: number; currency: Currency; firstBuyDate: string; + grossPerformance?: number; + grossPerformancePercentage?: number; investment: number; investmentInOriginalCurrency?: number; marketPrice?: number; + marketState?: MarketState; + name?: string; quantity: number; + symbol: string; transactionCount: number; + type?: Type; + url?: string; } diff --git a/libs/common/src/lib/interfaces/timeline-position.interface.ts b/libs/common/src/lib/interfaces/timeline-position.interface.ts index ea6028923..533c09a04 100644 --- a/libs/common/src/lib/interfaces/timeline-position.interface.ts +++ b/libs/common/src/lib/interfaces/timeline-position.interface.ts @@ -1,7 +1,3 @@ -import { - MarketState, - Type -} from '@ghostfolio/api/services/interfaces/interfaces'; import { Currency } from '@prisma/client'; import Big from 'big.js'; @@ -9,15 +5,11 @@ export interface TimelinePosition { averagePrice: Big; currency: Currency; firstBuyDate: string; - marketState: MarketState; - quantity: Big; - symbol: string; + grossPerformance: Big; + grossPerformancePercentage: Big; investment: Big; - grossPerformancePercentage: Big | number; // TODO - grossPerformance: Big | number; // TODO marketPrice: number; + quantity: Big; + symbol: string; transactionCount: number; - name: string; - url: string; - type: Type; }