diff --git a/CHANGELOG.md b/CHANGELOG.md index 6abb6119b..bca053c0c 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 +- Migrated the get country and sector weightings, dividends, ETF holdings, ETF info, historical price, profile, quote and symbol search functionalities of the _Financial Modeling Prep_ service to its stable API version - Refactored the toggle component to standalone - Improved the language localization for Dutch (`nl`) - Improved the language localization for Portuguese (`pt`) 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 a9f171f13..2dcb689a7 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 @@ -46,7 +46,6 @@ import { @Injectable() export class FinancialModelingPrepService implements DataProviderInterface { private apiKey: string; - private readonly URL = this.getUrl({ version: 3 }); public constructor( private readonly configurationService: ConfigurationService, @@ -81,7 +80,7 @@ export class FinancialModelingPrepService implements DataProviderInterface { ); } else if (this.cryptocurrencyService.isCryptocurrency(symbol)) { const [quote] = await fetch( - `${this.URL}/quote/${symbol}?apikey=${this.apiKey}`, + `${this.getUrl({ version: 'stable' })}/quote?symbol=${symbol}&apikey=${this.apiKey}`, { signal: AbortSignal.timeout(requestTimeout) } @@ -95,7 +94,7 @@ export class FinancialModelingPrepService implements DataProviderInterface { response.name = quote.name; } else { const [assetProfile] = await fetch( - `${this.URL}/profile/${symbol}?apikey=${this.apiKey}`, + `${this.getUrl({ version: 'stable' })}/profile?symbol=${symbol}&apikey=${this.apiKey}`, { signal: AbortSignal.timeout(requestTimeout) } @@ -109,7 +108,7 @@ export class FinancialModelingPrepService implements DataProviderInterface { if (assetSubClass === AssetSubClass.ETF) { const etfCountryWeightings = await fetch( - `${this.URL}/etf-country-weightings/${symbol}?apikey=${this.apiKey}`, + `${this.getUrl({ version: 'stable' })}/etf/country-weightings?symbol=${symbol}&apikey=${this.apiKey}`, { signal: AbortSignal.timeout(requestTimeout) } @@ -133,45 +132,38 @@ export class FinancialModelingPrepService implements DataProviderInterface { } ); - const [etfInformation] = await fetch( - `${this.getUrl({ version: 4 })}/etf-info?symbol=${symbol}&apikey=${this.apiKey}`, + const etfHoldings = await fetch( + `${this.getUrl({ version: 'stable' })}/etf/holdings?symbol=${symbol}&apikey=${this.apiKey}`, { signal: AbortSignal.timeout(requestTimeout) } ).then((res) => res.json()); - if (etfInformation.website) { - response.url = etfInformation.website; - } + const sortedTopHoldings = etfHoldings + .sort((a, b) => { + return b.weightPercentage - a.weightPercentage; + }) + .slice(0, 10); - const [portfolioDate] = await fetch( - `${this.getUrl({ version: 4 })}/etf-holdings/portfolio-date?symbol=${symbol}&apikey=${this.apiKey}`, + response.holdings = sortedTopHoldings.map( + ({ name, weightPercentage }) => { + return { name, weight: weightPercentage / 100 }; + } + ); + + const [etfInformation] = await fetch( + `${this.getUrl({ version: 'stable' })}/etf/info?symbol=${symbol}&apikey=${this.apiKey}`, { signal: AbortSignal.timeout(requestTimeout) } ).then((res) => res.json()); - if (portfolioDate) { - const etfHoldings = await fetch( - `${this.getUrl({ version: 4 })}/etf-holdings?date=${portfolioDate.date}&symbol=${symbol}&apikey=${this.apiKey}`, - { - signal: AbortSignal.timeout(requestTimeout) - } - ).then((res) => res.json()); - - const sortedTopHoldings = etfHoldings - .sort((a, b) => { - return b.pctVal - a.pctVal; - }) - .slice(0, 10); - - response.holdings = sortedTopHoldings.map(({ name, pctVal }) => { - return { name, weight: pctVal / 100 }; - }); + if (etfInformation.website) { + response.url = etfInformation.website; } const etfSectorWeightings = await fetch( - `${this.URL}/etf-sector-weightings/${symbol}?apikey=${this.apiKey}`, + `${this.getUrl({ version: 'stable' })}/etf/sector-weightings?symbol=${symbol}&apikey=${this.apiKey}`, { signal: AbortSignal.timeout(requestTimeout) } @@ -181,7 +173,7 @@ export class FinancialModelingPrepService implements DataProviderInterface { ({ sector, weightPercentage }) => { return { name: sector, - weight: parseFloat(weightPercentage.slice(0, -1)) / 100 + weight: weightPercentage / 100 }; } ); @@ -246,14 +238,14 @@ export class FinancialModelingPrepService implements DataProviderInterface { [date: string]: IDataProviderHistoricalResponse; } = {}; - const { historical = [] } = await fetch( - `${this.URL}/historical-price-full/stock_dividend/${symbol}?apikey=${this.apiKey}`, + const dividends = await fetch( + `${this.getUrl({ version: 'stable' })}/dividends?symbol=${symbol}&apikey=${this.apiKey}`, { signal: AbortSignal.timeout(requestTimeout) } ).then((res) => res.json()); - historical + dividends .filter(({ date }) => { return ( (isSameDay(parseISO(date), from) || @@ -307,21 +299,21 @@ export class FinancialModelingPrepService implements DataProviderInterface { ? addYears(currentFrom, MAX_YEARS_PER_REQUEST) : to; - const { historical = [] } = await fetch( - `${this.URL}/historical-price-full/${symbol}?apikey=${this.apiKey}&from=${format(currentFrom, DATE_FORMAT)}&to=${format(currentTo, DATE_FORMAT)}`, + const historical = await fetch( + `${this.getUrl({ version: 'stable' })}/historical-price-eod/full?symbol=${symbol}&apikey=${this.apiKey}&from=${format(currentFrom, DATE_FORMAT)}&to=${format(currentTo, DATE_FORMAT)}`, { signal: AbortSignal.timeout(requestTimeout) } ).then((res) => res.json()); - for (const { adjClose, date } of historical) { + for (const { close, date } of historical) { if ( (isSameDay(parseDate(date), currentFrom) || isAfter(parseDate(date), currentFrom)) && isBefore(parseDate(date), currentTo) ) { result[symbol][date] = { - marketPrice: adjClose + marketPrice: close }; } } @@ -454,7 +446,7 @@ export class FinancialModelingPrepService implements DataProviderInterface { }); } else { const result = await fetch( - `${this.URL}/search?query=${query}&apikey=${this.apiKey}`, + `${this.getUrl({ version: 'stable' })}/search-symbol?query=${query}&apikey=${this.apiKey}`, { signal: AbortSignal.timeout( this.configurationService.get('REQUEST_TIMEOUT') diff --git a/apps/client/src/app/pages/api/api-page.component.ts b/apps/client/src/app/pages/api/api-page.component.ts index 7b385ec2f..87fc164af 100644 --- a/apps/client/src/app/pages/api/api-page.component.ts +++ b/apps/client/src/app/pages/api/api-page.component.ts @@ -44,7 +44,7 @@ export class GfApiPageComponent implements OnInit { this.historicalData$ = this.fetchHistoricalData({ symbol: 'AAPL' }); this.isinLookupItems$ = this.fetchLookupItems({ query: 'US0378331005' }); this.lookupItems$ = this.fetchLookupItems({ query: 'apple' }); - this.quotes$ = this.fetchQuotes({ symbols: ['AAPL', 'VOO.US'] }); + this.quotes$ = this.fetchQuotes({ symbols: ['AAPL', 'VOO'] }); this.status$ = this.fetchStatus(); }