diff --git a/CHANGELOG.md b/CHANGELOG.md index 529d6f83a..cee2662a2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - Disabled the zoom functionality in the _Progressive Web App_ (PWA) +- Optimized the get quotes functionality by utilizing the asset profile resolutions in the _Financial Modeling Prep_ service ## 2.208.0 - 2025-10-11 diff --git a/apps/api/src/services/data-provider/financial-modeling-prep/financial-modeling-prep.service.ts b/apps/api/src/services/data-provider/financial-modeling-prep/financial-modeling-prep.service.ts index 8bb8f8327..3b0d8ba72 100644 --- a/apps/api/src/services/data-provider/financial-modeling-prep/financial-modeling-prep.service.ts +++ b/apps/api/src/services/data-provider/financial-modeling-prep/financial-modeling-prep.service.ts @@ -12,6 +12,7 @@ import { IDataProviderHistoricalResponse, IDataProviderResponse } from '@ghostfolio/api/services/interfaces/interfaces'; +import { PrismaService } from '@ghostfolio/api/services/prisma/prisma.service'; import { DEFAULT_CURRENCY, REPLACE_NAME_PARTS @@ -49,7 +50,8 @@ export class FinancialModelingPrepService implements DataProviderInterface { public constructor( private readonly configurationService: ConfigurationService, - private readonly cryptocurrencyService: CryptocurrencyService + private readonly cryptocurrencyService: CryptocurrencyService, + private readonly prismaService: PrismaService ) { this.apiKey = this.configurationService.get( 'API_KEY_FINANCIAL_MODELING_PREP' @@ -220,7 +222,7 @@ export class FinancialModelingPrepService implements DataProviderInterface { public getDataProviderInfo(): DataProviderInfo { return { - dataSource: DataSource.FINANCIAL_MODELING_PREP, + dataSource: this.getName(), isPremium: true, name: 'Financial Modeling Prep', url: 'https://financialmodelingprep.com/developer/docs' @@ -359,25 +361,41 @@ export class FinancialModelingPrepService implements DataProviderInterface { [symbol: string]: Pick; } = {}; - const quotes = await fetch( - `${this.getUrl({ version: 'stable' })}/batch-quote-short?symbols=${symbols.join(',')}&apikey=${this.apiKey}`, - { - signal: AbortSignal.timeout(requestTimeout) - } - ).then((res) => res.json()); + const [assetProfileResolutions, quotes] = await Promise.all([ + this.prismaService.assetProfileResolution.findMany({ + where: { + dataSourceTarget: this.getDataProviderInfo().dataSource, + symbolTarget: { in: symbols } + } + }), + fetch( + `${this.getUrl({ version: 'stable' })}/batch-quote-short?symbols=${symbols.join(',')}&apikey=${this.apiKey}`, + { + signal: AbortSignal.timeout(requestTimeout) + } + ).then((res) => res.json()) + ]); - await Promise.all( - quotes.map(({ symbol }) => { - return this.getAssetProfile({ - requestTimeout, - symbol - }).then((assetProfile) => { - if (assetProfile?.currency) { - currencyBySymbolMap[symbol] = { currency: assetProfile.currency }; - } - }); - }) - ); + if (assetProfileResolutions.length === symbols.length) { + for (const { currency, symbolTarget } of assetProfileResolutions) { + currencyBySymbolMap[symbolTarget] = { currency }; + } + } else { + await Promise.all( + quotes.map(({ symbol }) => { + return this.getAssetProfile({ + requestTimeout, + symbol + }).then((assetProfile) => { + if (assetProfile?.currency) { + currencyBySymbolMap[symbol] = { + currency: assetProfile.currency + }; + } + }); + }) + ); + } for (const { price, symbol } of quotes) { let marketState: MarketState = 'delayed'; @@ -394,7 +412,7 @@ export class FinancialModelingPrepService implements DataProviderInterface { marketState, currency: currencyBySymbolMap[symbol]?.currency, dataProviderInfo: this.getDataProviderInfo(), - dataSource: DataSource.FINANCIAL_MODELING_PREP, + dataSource: this.getDataProviderInfo().dataSource, marketPrice: price }; }