From b36d57514cb0f68940ee3370b66e6749bdba256a Mon Sep 17 00:00:00 2001 From: KenTandrian Date: Wed, 30 Apr 2025 22:41:03 +0700 Subject: [PATCH] feat(api): extend with performances data --- .../endpoints/watchlist/watchlist.module.ts | 4 ++ .../endpoints/watchlist/watchlist.service.ts | 40 +++++++++++++++++-- .../responses/watchlist-response.interface.ts | 9 ++++- 3 files changed, 48 insertions(+), 5 deletions(-) diff --git a/apps/api/src/app/endpoints/watchlist/watchlist.module.ts b/apps/api/src/app/endpoints/watchlist/watchlist.module.ts index 2addd2de0..a2d32d1d2 100644 --- a/apps/api/src/app/endpoints/watchlist/watchlist.module.ts +++ b/apps/api/src/app/endpoints/watchlist/watchlist.module.ts @@ -1,6 +1,8 @@ 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 { BenchmarkModule } from '@ghostfolio/api/services/benchmark/benchmark.module'; import { DataProviderModule } from '@ghostfolio/api/services/data-provider/data-provider.module'; +import { MarketDataModule } from '@ghostfolio/api/services/market-data/market-data.module'; import { PrismaModule } from '@ghostfolio/api/services/prisma/prisma.module'; import { DataGatheringModule } from '@ghostfolio/api/services/queues/data-gathering/data-gathering.module'; import { SymbolProfileModule } from '@ghostfolio/api/services/symbol-profile/symbol-profile.module'; @@ -13,8 +15,10 @@ import { WatchlistService } from './watchlist.service'; @Module({ controllers: [WatchlistController], imports: [ + BenchmarkModule, DataGatheringModule, DataProviderModule, + MarketDataModule, PrismaModule, SymbolProfileModule, TransformDataSourceInRequestModule, diff --git a/apps/api/src/app/endpoints/watchlist/watchlist.service.ts b/apps/api/src/app/endpoints/watchlist/watchlist.service.ts index 6ff71ec50..397f97a9a 100644 --- a/apps/api/src/app/endpoints/watchlist/watchlist.service.ts +++ b/apps/api/src/app/endpoints/watchlist/watchlist.service.ts @@ -1,17 +1,22 @@ +import { BenchmarkService } from '@ghostfolio/api/services/benchmark/benchmark.service'; import { DataProviderService } from '@ghostfolio/api/services/data-provider/data-provider.service'; +import { MarketDataService } from '@ghostfolio/api/services/market-data/market-data.service'; import { PrismaService } from '@ghostfolio/api/services/prisma/prisma.service'; import { DataGatheringService } from '@ghostfolio/api/services/queues/data-gathering/data-gathering.service'; import { SymbolProfileService } from '@ghostfolio/api/services/symbol-profile/symbol-profile.service'; -import { AssetProfileIdentifier } from '@ghostfolio/common/interfaces'; +import { WatchlistResponse } from '@ghostfolio/common/interfaces'; import { BadRequestException, Injectable } from '@nestjs/common'; import { DataSource, Prisma } from '@prisma/client'; +import ms from 'ms'; @Injectable() export class WatchlistService { public constructor( + private readonly benchmarkService: BenchmarkService, private readonly dataGatheringService: DataGatheringService, private readonly dataProviderService: DataProviderService, + private readonly marketDataService: MarketDataService, private readonly prismaService: PrismaService, private readonly symbolProfileService: SymbolProfileService ) {} @@ -87,7 +92,7 @@ export class WatchlistService { public async getWatchlistItems( userId: string - ): Promise { + ): Promise { const user = await this.prismaService.user.findUnique({ select: { watchlist: { @@ -97,6 +102,35 @@ export class WatchlistService { where: { id: userId } }); - return user.watchlist ?? []; + const quotes = await this.dataProviderService.getQuotes({ + items: user.watchlist.map(({ dataSource, symbol }) => { + return { dataSource, symbol }; + }), + requestTimeout: ms('30 seconds'), + useCache: false + }); + + const watchlist = await Promise.all( + user.watchlist.map(async ({ dataSource, symbol }) => { + const ath = await this.marketDataService.getMax({ dataSource, symbol }); + const performancePercent = + this.benchmarkService.calculateChangeInPercentage( + ath.marketPrice, + quotes[symbol]?.marketPrice + ); + return { + dataSource, + symbol, + performances: { + allTimeHigh: { + date: ath?.date, + performancePercent + } + } + }; + }) + ); + + return watchlist; } } diff --git a/libs/common/src/lib/interfaces/responses/watchlist-response.interface.ts b/libs/common/src/lib/interfaces/responses/watchlist-response.interface.ts index 3cdc834b4..8adecc9c5 100644 --- a/libs/common/src/lib/interfaces/responses/watchlist-response.interface.ts +++ b/libs/common/src/lib/interfaces/responses/watchlist-response.interface.ts @@ -1,5 +1,10 @@ -import { AssetProfileIdentifier } from '@ghostfolio/common/interfaces'; +import { + AssetProfileIdentifier, + Benchmark +} from '@ghostfolio/common/interfaces'; export interface WatchlistResponse { - watchlist: AssetProfileIdentifier[]; + watchlist: (AssetProfileIdentifier & { + performances: Benchmark['performances']; + })[]; }