Browse Source

Feature/migrate various requests to its stable API version in Financial Modeling Prep service (#5253)

* Migrate to stable API version

* Update changelog
pull/5243/head^2
Thomas Kaul 2 weeks ago
committed by GitHub
parent
commit
c8c5842cec
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 1
      CHANGELOG.md
  2. 64
      apps/api/src/services/data-provider/financial-modeling-prep/financial-modeling-prep.service.ts
  3. 2
      apps/client/src/app/pages/api/api-page.component.ts

1
CHANGELOG.md

@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed ### 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 - Refactored the toggle component to standalone
- Improved the language localization for Dutch (`nl`) - Improved the language localization for Dutch (`nl`)
- Improved the language localization for Portuguese (`pt`) - Improved the language localization for Portuguese (`pt`)

64
apps/api/src/services/data-provider/financial-modeling-prep/financial-modeling-prep.service.ts

@ -46,7 +46,6 @@ import {
@Injectable() @Injectable()
export class FinancialModelingPrepService implements DataProviderInterface { export class FinancialModelingPrepService implements DataProviderInterface {
private apiKey: string; private apiKey: string;
private readonly URL = this.getUrl({ version: 3 });
public constructor( public constructor(
private readonly configurationService: ConfigurationService, private readonly configurationService: ConfigurationService,
@ -81,7 +80,7 @@ export class FinancialModelingPrepService implements DataProviderInterface {
); );
} else if (this.cryptocurrencyService.isCryptocurrency(symbol)) { } else if (this.cryptocurrencyService.isCryptocurrency(symbol)) {
const [quote] = await fetch( 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) signal: AbortSignal.timeout(requestTimeout)
} }
@ -95,7 +94,7 @@ export class FinancialModelingPrepService implements DataProviderInterface {
response.name = quote.name; response.name = quote.name;
} else { } else {
const [assetProfile] = await fetch( 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) signal: AbortSignal.timeout(requestTimeout)
} }
@ -109,7 +108,7 @@ export class FinancialModelingPrepService implements DataProviderInterface {
if (assetSubClass === AssetSubClass.ETF) { if (assetSubClass === AssetSubClass.ETF) {
const etfCountryWeightings = await fetch( 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) signal: AbortSignal.timeout(requestTimeout)
} }
@ -133,45 +132,38 @@ export class FinancialModelingPrepService implements DataProviderInterface {
} }
); );
const [etfInformation] = await fetch( const etfHoldings = await fetch(
`${this.getUrl({ version: 4 })}/etf-info?symbol=${symbol}&apikey=${this.apiKey}`, `${this.getUrl({ version: 'stable' })}/etf/holdings?symbol=${symbol}&apikey=${this.apiKey}`,
{ {
signal: AbortSignal.timeout(requestTimeout) signal: AbortSignal.timeout(requestTimeout)
} }
).then((res) => res.json()); ).then((res) => res.json());
if (etfInformation.website) { const sortedTopHoldings = etfHoldings
response.url = etfInformation.website; .sort((a, b) => {
} return b.weightPercentage - a.weightPercentage;
})
.slice(0, 10);
const [portfolioDate] = await fetch( response.holdings = sortedTopHoldings.map(
`${this.getUrl({ version: 4 })}/etf-holdings/portfolio-date?symbol=${symbol}&apikey=${this.apiKey}`, ({ name, weightPercentage }) => {
{ return { name, weight: weightPercentage / 100 };
signal: AbortSignal.timeout(requestTimeout)
} }
).then((res) => res.json()); );
if (portfolioDate) { const [etfInformation] = await fetch(
const etfHoldings = await fetch( `${this.getUrl({ version: 'stable' })}/etf/info?symbol=${symbol}&apikey=${this.apiKey}`,
`${this.getUrl({ version: 4 })}/etf-holdings?date=${portfolioDate.date}&symbol=${symbol}&apikey=${this.apiKey}`,
{ {
signal: AbortSignal.timeout(requestTimeout) signal: AbortSignal.timeout(requestTimeout)
} }
).then((res) => res.json()); ).then((res) => res.json());
const sortedTopHoldings = etfHoldings if (etfInformation.website) {
.sort((a, b) => { response.url = etfInformation.website;
return b.pctVal - a.pctVal;
})
.slice(0, 10);
response.holdings = sortedTopHoldings.map(({ name, pctVal }) => {
return { name, weight: pctVal / 100 };
});
} }
const etfSectorWeightings = await fetch( 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) signal: AbortSignal.timeout(requestTimeout)
} }
@ -181,7 +173,7 @@ export class FinancialModelingPrepService implements DataProviderInterface {
({ sector, weightPercentage }) => { ({ sector, weightPercentage }) => {
return { return {
name: sector, name: sector,
weight: parseFloat(weightPercentage.slice(0, -1)) / 100 weight: weightPercentage / 100
}; };
} }
); );
@ -246,14 +238,14 @@ export class FinancialModelingPrepService implements DataProviderInterface {
[date: string]: IDataProviderHistoricalResponse; [date: string]: IDataProviderHistoricalResponse;
} = {}; } = {};
const { historical = [] } = await fetch( const dividends = await fetch(
`${this.URL}/historical-price-full/stock_dividend/${symbol}?apikey=${this.apiKey}`, `${this.getUrl({ version: 'stable' })}/dividends?symbol=${symbol}&apikey=${this.apiKey}`,
{ {
signal: AbortSignal.timeout(requestTimeout) signal: AbortSignal.timeout(requestTimeout)
} }
).then((res) => res.json()); ).then((res) => res.json());
historical dividends
.filter(({ date }) => { .filter(({ date }) => {
return ( return (
(isSameDay(parseISO(date), from) || (isSameDay(parseISO(date), from) ||
@ -307,21 +299,21 @@ export class FinancialModelingPrepService implements DataProviderInterface {
? addYears(currentFrom, MAX_YEARS_PER_REQUEST) ? addYears(currentFrom, MAX_YEARS_PER_REQUEST)
: to; : to;
const { historical = [] } = await fetch( const historical = await fetch(
`${this.URL}/historical-price-full/${symbol}?apikey=${this.apiKey}&from=${format(currentFrom, DATE_FORMAT)}&to=${format(currentTo, DATE_FORMAT)}`, `${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) signal: AbortSignal.timeout(requestTimeout)
} }
).then((res) => res.json()); ).then((res) => res.json());
for (const { adjClose, date } of historical) { for (const { close, date } of historical) {
if ( if (
(isSameDay(parseDate(date), currentFrom) || (isSameDay(parseDate(date), currentFrom) ||
isAfter(parseDate(date), currentFrom)) && isAfter(parseDate(date), currentFrom)) &&
isBefore(parseDate(date), currentTo) isBefore(parseDate(date), currentTo)
) { ) {
result[symbol][date] = { result[symbol][date] = {
marketPrice: adjClose marketPrice: close
}; };
} }
} }
@ -454,7 +446,7 @@ export class FinancialModelingPrepService implements DataProviderInterface {
}); });
} else { } else {
const result = await fetch( 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( signal: AbortSignal.timeout(
this.configurationService.get('REQUEST_TIMEOUT') this.configurationService.get('REQUEST_TIMEOUT')

2
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.historicalData$ = this.fetchHistoricalData({ symbol: 'AAPL' });
this.isinLookupItems$ = this.fetchLookupItems({ query: 'US0378331005' }); this.isinLookupItems$ = this.fetchLookupItems({ query: 'US0378331005' });
this.lookupItems$ = this.fetchLookupItems({ query: 'apple' }); 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(); this.status$ = this.fetchStatus();
} }

Loading…
Cancel
Save