Browse Source
Task/simplify getHistorical() function response in data provider interface (#7369)
* Simplify getHistorical() function response
* Update changelog
pull/7371/head^2
Thomas Kaul
22 hours ago
committed by
GitHub
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with
52 additions and
66 deletions
-
CHANGELOG.md
-
apps/api/src/app/endpoints/data-providers/ghostfolio/ghostfolio.service.ts
-
apps/api/src/services/data-provider/alpha-vantage/alpha-vantage.service.ts
-
apps/api/src/services/data-provider/coingecko/coingecko.service.ts
-
apps/api/src/services/data-provider/data-provider.service.ts
-
apps/api/src/services/data-provider/eod-historical-data/eod-historical-data.service.ts
-
apps/api/src/services/data-provider/financial-modeling-prep/financial-modeling-prep.service.ts
-
apps/api/src/services/data-provider/ghostfolio/ghostfolio.service.ts
-
apps/api/src/services/data-provider/google-sheets/google-sheets.service.ts
-
apps/api/src/services/data-provider/interfaces/data-provider.interface.ts
-
apps/api/src/services/data-provider/manual/manual.service.ts
-
apps/api/src/services/data-provider/rapid-api/rapid-api.service.ts
-
apps/api/src/services/data-provider/yahoo-finance/yahoo-finance.service.ts
|
|
|
@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file. |
|
|
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), |
|
|
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). |
|
|
|
|
|
|
|
## Unreleased |
|
|
|
|
|
|
|
### Changed |
|
|
|
|
|
|
|
- Simplified the `getHistorical()` function response in the data provider interface |
|
|
|
|
|
|
|
## 3.29.0 - 2026-07-18 |
|
|
|
|
|
|
|
### Added |
|
|
|
|
|
|
|
@ -171,7 +171,7 @@ export class GhostfolioService { |
|
|
|
|
|
|
|
try { |
|
|
|
const promises: Promise<{ |
|
|
|
[symbol: string]: { [date: string]: DataProviderHistoricalResponse }; |
|
|
|
[date: string]: DataProviderHistoricalResponse; |
|
|
|
}>[] = []; |
|
|
|
|
|
|
|
for (const dataProviderService of this.getDataProviderServices()) { |
|
|
|
@ -185,7 +185,7 @@ export class GhostfolioService { |
|
|
|
to |
|
|
|
}) |
|
|
|
.then((historicalData) => { |
|
|
|
result.historicalData = historicalData[symbol]; |
|
|
|
result.historicalData = historicalData; |
|
|
|
|
|
|
|
return historicalData; |
|
|
|
}) |
|
|
|
|
|
|
|
@ -70,7 +70,7 @@ export class AlphaVantageService |
|
|
|
symbol, |
|
|
|
to |
|
|
|
}: GetHistoricalParams): Promise<{ |
|
|
|
[symbol: string]: { [date: string]: DataProviderHistoricalResponse }; |
|
|
|
[date: string]: DataProviderHistoricalResponse; |
|
|
|
}> { |
|
|
|
try { |
|
|
|
const historicalData: { |
|
|
|
@ -83,11 +83,9 @@ export class AlphaVantageService |
|
|
|
); |
|
|
|
|
|
|
|
const response: { |
|
|
|
[symbol: string]: { [date: string]: DataProviderHistoricalResponse }; |
|
|
|
[date: string]: DataProviderHistoricalResponse; |
|
|
|
} = {}; |
|
|
|
|
|
|
|
response[symbol] = {}; |
|
|
|
|
|
|
|
for (const [key, timeSeries] of Object.entries( |
|
|
|
historicalData['Time Series (Digital Currency Daily)'] |
|
|
|
).sort()) { |
|
|
|
@ -95,7 +93,7 @@ export class AlphaVantageService |
|
|
|
isAfter(from, parse(key, DATE_FORMAT, new Date())) && |
|
|
|
isBefore(to, parse(key, DATE_FORMAT, new Date())) |
|
|
|
) { |
|
|
|
response[symbol][key] = { |
|
|
|
response[key] = { |
|
|
|
marketPrice: parseFloat(timeSeries['4a. close (USD)']) |
|
|
|
}; |
|
|
|
} |
|
|
|
|
|
|
|
@ -115,7 +115,7 @@ export class CoinGeckoService implements DataProviderInterface, OnModuleInit { |
|
|
|
symbol, |
|
|
|
to |
|
|
|
}: GetHistoricalParams): Promise<{ |
|
|
|
[symbol: string]: { [date: string]: DataProviderHistoricalResponse }; |
|
|
|
[date: string]: DataProviderHistoricalResponse; |
|
|
|
}> { |
|
|
|
try { |
|
|
|
const queryParams = new URLSearchParams({ |
|
|
|
@ -143,13 +143,11 @@ export class CoinGeckoService implements DataProviderInterface, OnModuleInit { |
|
|
|
} |
|
|
|
|
|
|
|
const result: { |
|
|
|
[symbol: string]: { [date: string]: DataProviderHistoricalResponse }; |
|
|
|
} = { |
|
|
|
[symbol]: {} |
|
|
|
}; |
|
|
|
[date: string]: DataProviderHistoricalResponse; |
|
|
|
} = {}; |
|
|
|
|
|
|
|
for (const [timestamp, marketPrice] of prices) { |
|
|
|
result[symbol][format(fromUnixTime(timestamp / 1000), DATE_FORMAT)] = { |
|
|
|
result[format(fromUnixTime(timestamp / 1000), DATE_FORMAT)] = { |
|
|
|
marketPrice |
|
|
|
}; |
|
|
|
} |
|
|
|
|
|
|
|
@ -508,7 +508,7 @@ export class DataProviderService implements OnModuleInit { |
|
|
|
requestTimeout: ms('30 seconds') |
|
|
|
}) |
|
|
|
.then((data) => { |
|
|
|
return { dataSource, symbol, data: data?.[symbol] }; |
|
|
|
return { data, dataSource, symbol }; |
|
|
|
}) |
|
|
|
); |
|
|
|
} |
|
|
|
|
|
|
|
@ -147,7 +147,7 @@ export class EodHistoricalDataService |
|
|
|
symbol, |
|
|
|
to |
|
|
|
}: GetHistoricalParams): Promise<{ |
|
|
|
[symbol: string]: { [date: string]: DataProviderHistoricalResponse }; |
|
|
|
[date: string]: DataProviderHistoricalResponse; |
|
|
|
}> { |
|
|
|
symbol = this.convertToEodSymbol(symbol); |
|
|
|
|
|
|
|
@ -166,10 +166,9 @@ export class EodHistoricalDataService |
|
|
|
}) |
|
|
|
.then((res) => res.json()); |
|
|
|
|
|
|
|
return response.reduce( |
|
|
|
(result, { adjusted_close, date }) => { |
|
|
|
return response.reduce((result, { adjusted_close, date }) => { |
|
|
|
if (isNumber(adjusted_close)) { |
|
|
|
result[this.convertFromEodSymbol(symbol)][date] = { |
|
|
|
result[date] = { |
|
|
|
marketPrice: adjusted_close |
|
|
|
}; |
|
|
|
} else { |
|
|
|
@ -179,9 +178,7 @@ export class EodHistoricalDataService |
|
|
|
} |
|
|
|
|
|
|
|
return result; |
|
|
|
}, |
|
|
|
{ [this.convertFromEodSymbol(symbol)]: {} } |
|
|
|
); |
|
|
|
}, {}); |
|
|
|
} catch (error) { |
|
|
|
throw new Error( |
|
|
|
`Could not get historical market data for ${symbol} (${this.getName()}) from ${format( |
|
|
|
|
|
|
|
@ -336,14 +336,12 @@ export class FinancialModelingPrepService |
|
|
|
symbol, |
|
|
|
to |
|
|
|
}: GetHistoricalParams): Promise<{ |
|
|
|
[symbol: string]: { [date: string]: DataProviderHistoricalResponse }; |
|
|
|
[date: string]: DataProviderHistoricalResponse; |
|
|
|
}> { |
|
|
|
const MAX_YEARS_PER_REQUEST = 5; |
|
|
|
const result: { |
|
|
|
[symbol: string]: { [date: string]: DataProviderHistoricalResponse }; |
|
|
|
} = { |
|
|
|
[symbol]: {} |
|
|
|
}; |
|
|
|
[date: string]: DataProviderHistoricalResponse; |
|
|
|
} = {}; |
|
|
|
|
|
|
|
let currentFrom = from; |
|
|
|
|
|
|
|
@ -378,7 +376,7 @@ export class FinancialModelingPrepService |
|
|
|
isAfter(parseDate(date), currentFrom)) && |
|
|
|
isBefore(parseDate(date), currentTo) |
|
|
|
) { |
|
|
|
result[symbol][date] = { |
|
|
|
result[date] = { |
|
|
|
marketPrice: close |
|
|
|
}; |
|
|
|
} |
|
|
|
|
|
|
|
@ -172,7 +172,7 @@ export class GhostfolioService implements DataProviderInterface { |
|
|
|
symbol, |
|
|
|
to |
|
|
|
}: GetHistoricalParams): Promise<{ |
|
|
|
[symbol: string]: { [date: string]: DataProviderHistoricalResponse }; |
|
|
|
[date: string]: DataProviderHistoricalResponse; |
|
|
|
}> { |
|
|
|
try { |
|
|
|
const queryParams = new URLSearchParams({ |
|
|
|
@ -198,9 +198,7 @@ export class GhostfolioService implements DataProviderInterface { |
|
|
|
|
|
|
|
const { historicalData } = (await response.json()) as HistoricalResponse; |
|
|
|
|
|
|
|
return { |
|
|
|
[symbol]: historicalData |
|
|
|
}; |
|
|
|
return historicalData; |
|
|
|
} catch (error) { |
|
|
|
if (error?.status === StatusCodes.TOO_MANY_REQUESTS) { |
|
|
|
error.name = 'RequestError'; |
|
|
|
|
|
|
|
@ -60,7 +60,7 @@ export class GoogleSheetsService implements DataProviderInterface { |
|
|
|
symbol, |
|
|
|
to |
|
|
|
}: GetHistoricalParams): Promise<{ |
|
|
|
[symbol: string]: { [date: string]: DataProviderHistoricalResponse }; |
|
|
|
[date: string]: DataProviderHistoricalResponse; |
|
|
|
}> { |
|
|
|
try { |
|
|
|
const sheet = await this.getSheet({ |
|
|
|
@ -85,9 +85,7 @@ export class GoogleSheetsService implements DataProviderInterface { |
|
|
|
historicalData[format(date, DATE_FORMAT)] = { marketPrice: close }; |
|
|
|
}); |
|
|
|
|
|
|
|
return { |
|
|
|
[symbol]: historicalData |
|
|
|
}; |
|
|
|
return historicalData; |
|
|
|
} catch (error) { |
|
|
|
throw new Error( |
|
|
|
`Could not get historical market data for ${symbol} (${this.getName()}) from ${format( |
|
|
|
|
|
|
|
@ -35,8 +35,8 @@ export interface DataProviderInterface { |
|
|
|
symbol, |
|
|
|
to |
|
|
|
}: GetHistoricalParams): Promise<{ |
|
|
|
[symbol: string]: { [date: string]: DataProviderHistoricalResponse }; |
|
|
|
}>; // TODO: Return only one symbol
|
|
|
|
[date: string]: DataProviderHistoricalResponse; |
|
|
|
}>; |
|
|
|
|
|
|
|
getMarketDataOfMarkets?({ |
|
|
|
includeHistoricalData, |
|
|
|
|
|
|
|
@ -79,7 +79,7 @@ export class ManualService implements DataProviderInterface { |
|
|
|
symbol, |
|
|
|
to |
|
|
|
}: GetHistoricalParams): Promise<{ |
|
|
|
[symbol: string]: { [date: string]: DataProviderHistoricalResponse }; |
|
|
|
[date: string]: DataProviderHistoricalResponse; |
|
|
|
}> { |
|
|
|
try { |
|
|
|
const [symbolProfile] = await this.symbolProfileService.getSymbolProfiles( |
|
|
|
@ -90,14 +90,13 @@ export class ManualService implements DataProviderInterface { |
|
|
|
|
|
|
|
if (defaultMarketPrice) { |
|
|
|
const historical: { |
|
|
|
[symbol: string]: { [date: string]: DataProviderHistoricalResponse }; |
|
|
|
} = { |
|
|
|
[symbol]: {} |
|
|
|
}; |
|
|
|
[date: string]: DataProviderHistoricalResponse; |
|
|
|
} = {}; |
|
|
|
|
|
|
|
let date = from; |
|
|
|
|
|
|
|
while (isBefore(date, to)) { |
|
|
|
historical[symbol][format(date, DATE_FORMAT)] = { |
|
|
|
historical[format(date, DATE_FORMAT)] = { |
|
|
|
marketPrice: defaultMarketPrice |
|
|
|
}; |
|
|
|
|
|
|
|
@ -115,11 +114,9 @@ export class ManualService implements DataProviderInterface { |
|
|
|
}); |
|
|
|
|
|
|
|
return { |
|
|
|
[symbol]: { |
|
|
|
[format(getYesterday(), DATE_FORMAT)]: { |
|
|
|
marketPrice: value |
|
|
|
} |
|
|
|
} |
|
|
|
}; |
|
|
|
} catch (error) { |
|
|
|
throw new Error( |
|
|
|
|
|
|
|
@ -58,7 +58,7 @@ export class RapidApiService implements DataProviderInterface { |
|
|
|
symbol, |
|
|
|
to |
|
|
|
}: GetHistoricalParams): Promise<{ |
|
|
|
[symbol: string]: { [date: string]: DataProviderHistoricalResponse }; |
|
|
|
[date: string]: DataProviderHistoricalResponse; |
|
|
|
}> { |
|
|
|
try { |
|
|
|
if (symbol === ghostfolioFearAndGreedIndexSymbolStocks) { |
|
|
|
@ -66,11 +66,9 @@ export class RapidApiService implements DataProviderInterface { |
|
|
|
|
|
|
|
if (fgi) { |
|
|
|
return { |
|
|
|
[symbol]: { |
|
|
|
[format(getYesterday(), DATE_FORMAT)]: { |
|
|
|
marketPrice: fgi.previousClose.value |
|
|
|
} |
|
|
|
} |
|
|
|
}; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
@ -123,7 +123,7 @@ export class YahooFinanceService implements DataProviderInterface { |
|
|
|
symbol, |
|
|
|
to |
|
|
|
}: GetHistoricalParams): Promise<{ |
|
|
|
[symbol: string]: { [date: string]: DataProviderHistoricalResponse }; |
|
|
|
[date: string]: DataProviderHistoricalResponse; |
|
|
|
}> { |
|
|
|
if (isSameDay(from, to)) { |
|
|
|
to = addDays(to, 1); |
|
|
|
@ -144,13 +144,11 @@ export class YahooFinanceService implements DataProviderInterface { |
|
|
|
); |
|
|
|
|
|
|
|
const response: { |
|
|
|
[symbol: string]: { [date: string]: DataProviderHistoricalResponse }; |
|
|
|
[date: string]: DataProviderHistoricalResponse; |
|
|
|
} = {}; |
|
|
|
|
|
|
|
response[symbol] = {}; |
|
|
|
|
|
|
|
for (const historicalItem of historicalResult) { |
|
|
|
response[symbol][format(historicalItem.date, DATE_FORMAT)] = { |
|
|
|
response[format(historicalItem.date, DATE_FORMAT)] = { |
|
|
|
marketPrice: historicalItem.close |
|
|
|
}; |
|
|
|
} |
|
|
|
|