From 080b794c8c7db3141e3f89f635e4a97530fdfdd5 Mon Sep 17 00:00:00 2001 From: KenTandrian Date: Mon, 5 May 2025 00:28:16 +0700 Subject: [PATCH] feat(api): add performances to holding response --- .../api/src/app/portfolio/portfolio.module.ts | 2 ++ .../src/app/portfolio/portfolio.service.ts | 34 +++++++++++++++++++ .../portfolio-holding-response.interface.ts | 2 ++ 3 files changed, 38 insertions(+) diff --git a/apps/api/src/app/portfolio/portfolio.module.ts b/apps/api/src/app/portfolio/portfolio.module.ts index 7ae74ee5f..0f64b2f6a 100644 --- a/apps/api/src/app/portfolio/portfolio.module.ts +++ b/apps/api/src/app/portfolio/portfolio.module.ts @@ -9,6 +9,7 @@ import { RedactValuesInResponseModule } from '@ghostfolio/api/interceptors/redac import { TransformDataSourceInRequestModule } from '@ghostfolio/api/interceptors/transform-data-source-in-request/transform-data-source-in-request.module'; import { TransformDataSourceInResponseModule } from '@ghostfolio/api/interceptors/transform-data-source-in-response/transform-data-source-in-response.module'; import { ApiModule } from '@ghostfolio/api/services/api/api.module'; +import { BenchmarkModule } from '@ghostfolio/api/services/benchmark/benchmark.module'; import { ConfigurationModule } from '@ghostfolio/api/services/configuration/configuration.module'; import { DataProviderModule } from '@ghostfolio/api/services/data-provider/data-provider.module'; import { ExchangeRateDataModule } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.module'; @@ -33,6 +34,7 @@ import { RulesService } from './rules.service'; imports: [ AccessModule, ApiModule, + BenchmarkModule, ConfigurationModule, DataGatheringModule, DataProviderModule, diff --git a/apps/api/src/app/portfolio/portfolio.service.ts b/apps/api/src/app/portfolio/portfolio.service.ts index 47c773e80..a6f3df6c3 100644 --- a/apps/api/src/app/portfolio/portfolio.service.ts +++ b/apps/api/src/app/portfolio/portfolio.service.ts @@ -20,9 +20,11 @@ import { RegionalMarketClusterRiskEmergingMarkets } from '@ghostfolio/api/models import { RegionalMarketClusterRiskEurope } from '@ghostfolio/api/models/rules/regional-market-cluster-risk/europe'; import { RegionalMarketClusterRiskJapan } from '@ghostfolio/api/models/rules/regional-market-cluster-risk/japan'; import { RegionalMarketClusterRiskNorthAmerica } from '@ghostfolio/api/models/rules/regional-market-cluster-risk/north-america'; +import { BenchmarkService } from '@ghostfolio/api/services/benchmark/benchmark.service'; import { DataProviderService } from '@ghostfolio/api/services/data-provider/data-provider.service'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; import { ImpersonationService } from '@ghostfolio/api/services/impersonation/impersonation.service'; +import { MarketDataService } from '@ghostfolio/api/services/market-data/market-data.service'; import { SymbolProfileService } from '@ghostfolio/api/services/symbol-profile/symbol-profile.service'; import { getAnnualizedPerformancePercent, @@ -100,10 +102,12 @@ export class PortfolioService { public constructor( private readonly accountBalanceService: AccountBalanceService, private readonly accountService: AccountService, + private readonly benchmarkService: BenchmarkService, private readonly calculatorFactory: PortfolioCalculatorFactory, private readonly dataProviderService: DataProviderService, private readonly exchangeRateDataService: ExchangeRateDataService, private readonly impersonationService: ImpersonationService, + private readonly marketDataService: MarketDataService, private readonly orderService: OrderService, @Inject(REQUEST) private readonly request: RequestWithUser, private readonly rulesService: RulesService, @@ -669,6 +673,7 @@ export class PortfolioService { netPerformancePercent: undefined, netPerformancePercentWithCurrencyEffect: undefined, netPerformanceWithCurrencyEffect: undefined, + performances: undefined, quantity: undefined, SymbolProfile: undefined, tags: [], @@ -677,6 +682,11 @@ export class PortfolioService { }; } + const allTimeHigh = await this.marketDataService.getMax({ + dataSource: aDataSource, + symbol: aSymbol + }); + const [SymbolProfile] = await this.symbolProfileService.getSymbolProfiles([ { dataSource: aDataSource, symbol: aSymbol } ]); @@ -809,6 +819,12 @@ export class PortfolioService { }); } + const performancePercent = + this.benchmarkService.calculateChangeInPercentage( + allTimeHigh?.marketPrice, + marketPrice + ); + return { firstBuyDate, marketPrice, @@ -846,6 +862,12 @@ export class PortfolioService { ]?.toNumber(), netPerformanceWithCurrencyEffect: position.netPerformanceWithCurrencyEffectMap?.['max']?.toNumber(), + performances: { + allTimeHigh: { + performancePercent, + date: allTimeHigh?.date + } + }, quantity: quantity.toNumber(), value: this.exchangeRateDataService.toCurrency( quantity.mul(marketPrice ?? 0).toNumber(), @@ -902,6 +924,12 @@ export class PortfolioService { ); } + const performancePercent = + this.benchmarkService.calculateChangeInPercentage( + allTimeHigh?.marketPrice, + marketPrice + ); + return { marketPrice, marketPriceMax, @@ -925,6 +953,12 @@ export class PortfolioService { netPerformancePercent: undefined, netPerformancePercentWithCurrencyEffect: undefined, netPerformanceWithCurrencyEffect: undefined, + performances: { + allTimeHigh: { + performancePercent, + date: allTimeHigh?.date + } + }, quantity: 0, tags: [], transactionCount: undefined, diff --git a/libs/common/src/lib/interfaces/responses/portfolio-holding-response.interface.ts b/libs/common/src/lib/interfaces/responses/portfolio-holding-response.interface.ts index c460242af..b330e8baa 100644 --- a/libs/common/src/lib/interfaces/responses/portfolio-holding-response.interface.ts +++ b/libs/common/src/lib/interfaces/responses/portfolio-holding-response.interface.ts @@ -1,5 +1,6 @@ import { Activity } from '@ghostfolio/api/app/order/interfaces/activities.interface'; import { + Benchmark, DataProviderInfo, EnhancedSymbolProfile, HistoricalDataItem @@ -29,6 +30,7 @@ export interface PortfolioHoldingResponse { netPerformancePercent: number; netPerformancePercentWithCurrencyEffect: number; netPerformanceWithCurrencyEffect: number; + performances: Benchmark['performances']; quantity: number; SymbolProfile: EnhancedSymbolProfile; tags: Tag[];