diff --git a/CHANGELOG.md b/CHANGELOG.md index 5d17200988..1ece439232 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added a tool to update the cash balance of an account to the server of the Model Context Protocol (MCP) (experimental) +### Changed + +- Improved the search by ISIN number in the _Financial Modeling Prep_ service + ## 3.87.0 - 2026-10-17 ### Added 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 e6ad639b84..3b38345efa 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 @@ -132,19 +132,7 @@ export class FinancialModelingPrepService ); response.name = quote.name; } else { - const queryParams = new URLSearchParams({ - symbol, - apikey: this.apiKey - }); - - const [assetProfile] = await this.fetchService - .fetch( - `${this.getUrl({ version: 'stable' })}/profile?${queryParams.toString()}`, - { - signal: AbortSignal.timeout(requestTimeout) - } - ) - .then((res) => res.json()); + const assetProfile = await this.getProfile({ requestTimeout, symbol }); if (!assetProfile) { const indexProfile = await this.getIndexProfile({ @@ -549,6 +537,10 @@ export class FinancialModelingPrepService return 'AAPL'; } + /** + * Financial Modeling Prep has no currency and no asset class in the search + * by ISIN, thus they are taken from the profile of the symbol. + */ public async search({ includeIndices = false, query, @@ -578,18 +570,30 @@ export class FinancialModelingPrepService await Promise.all( result.map(({ symbol }) => { - return this.getAssetProfile({ symbol }).then((assetProfile) => { - assetProfileBySymbolMap[symbol] = assetProfile; - }); + return this.getProfile({ requestTimeout, symbol }) + .then((profile) => { + assetProfileBySymbolMap[symbol] = { + ...this.parseAssetClass(profile), + currency: profile?.currency + }; + }) + .catch((error) => { + this.logger.error( + `Could not get the profile of ${symbol} (${this.getName()}): [${error?.name}] ${error?.message}` + ); + }); }) ); - items = result.map(({ assetClass, assetSubClass, name, symbol }) => { + items = result.map(({ name, symbol }) => { + const { assetClass, assetSubClass, currency } = + assetProfileBySymbolMap[symbol] ?? {}; + return { assetClass, assetSubClass, + currency, symbol, - currency: assetProfileBySymbolMap[symbol]?.currency, dataProviderInfo: this.getDataProviderInfo(), dataSource: this.getName(), name: this.formatName({ name }) @@ -696,6 +700,30 @@ export class FinancialModelingPrepService }; } + private async getProfile({ + requestTimeout, + symbol + }: { + requestTimeout: number; + symbol: string; + }) { + const queryParams = new URLSearchParams({ + symbol, + apikey: this.apiKey + }); + + const [profile] = await this.fetchService + .fetch( + `${this.getUrl({ version: 'stable' })}/profile?${queryParams.toString()}`, + { + signal: AbortSignal.timeout(requestTimeout) + } + ) + .then((res) => res.json()); + + return profile; + } + private async getSearchResults({ endpoint, query,