Feature/add search functionality for eod historical data (#1783)

* Add search functionality for EOD_HISTORICAL_DATA

* Update changelog
pull/1786/head
Thomas Kaul 2 years ago committed by GitHub
parent e37a34ed6c
commit 7c6ff776d9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## Unreleased ## Unreleased
### Added
- Added the search functionality for the `EOD_HISTORICAL_DATA` data source type
### Changed ### Changed
- Improved the usability of the _FIRE_ calculator - Improved the usability of the _FIRE_ calculator

@ -272,14 +272,20 @@ export class DataProviderService {
const searchResults = await Promise.all(promises); const searchResults = await Promise.all(promises);
searchResults.forEach((searchResult) => { searchResults.forEach(({ items }) => {
lookupItems = lookupItems.concat(searchResult.items); if (items?.length > 0) {
lookupItems = lookupItems.concat(items);
}
}); });
const filteredItems = lookupItems.filter((lookupItem) => { const filteredItems = lookupItems
// Only allow symbols with supported currency .filter((lookupItem) => {
return lookupItem.currency ? true : false; // Only allow symbols with supported currency
}); return lookupItem.currency ? true : false;
})
.sort(({ name: name1 }, { name: name2 }) => {
return name1?.toLowerCase().localeCompare(name2?.toLowerCase());
});
return { return {
items: filteredItems items: filteredItems

@ -5,13 +5,12 @@ import {
IDataProviderHistoricalResponse, IDataProviderHistoricalResponse,
IDataProviderResponse IDataProviderResponse
} from '@ghostfolio/api/services/interfaces/interfaces'; } from '@ghostfolio/api/services/interfaces/interfaces';
import { SymbolProfileService } from '@ghostfolio/api/services/symbol-profile.service';
import { DATE_FORMAT } from '@ghostfolio/common/helper'; import { DATE_FORMAT } from '@ghostfolio/common/helper';
import { Granularity } from '@ghostfolio/common/types'; import { Granularity } from '@ghostfolio/common/types';
import { Injectable, Logger } from '@nestjs/common'; import { Injectable, Logger } from '@nestjs/common';
import { DataSource, SymbolProfile } from '@prisma/client'; import { DataSource, SymbolProfile } from '@prisma/client';
import bent from 'bent'; import bent from 'bent';
import { format } from 'date-fns'; import { format, isToday } from 'date-fns';
@Injectable() @Injectable()
export class EodHistoricalDataService implements DataProviderInterface { export class EodHistoricalDataService implements DataProviderInterface {
@ -19,8 +18,7 @@ export class EodHistoricalDataService implements DataProviderInterface {
private readonly URL = 'https://eodhistoricaldata.com/api'; private readonly URL = 'https://eodhistoricaldata.com/api';
public constructor( public constructor(
private readonly configurationService: ConfigurationService, private readonly configurationService: ConfigurationService
private readonly symbolProfileService: SymbolProfileService
) { ) {
this.apiKey = this.configurationService.get('EOD_HISTORICAL_DATA_API_KEY'); this.apiKey = this.configurationService.get('EOD_HISTORICAL_DATA_API_KEY');
} }
@ -32,8 +30,12 @@ export class EodHistoricalDataService implements DataProviderInterface {
public async getAssetProfile( public async getAssetProfile(
aSymbol: string aSymbol: string
): Promise<Partial<SymbolProfile>> { ): Promise<Partial<SymbolProfile>> {
const { items } = await this.search(aSymbol);
return { return {
dataSource: this.getName() currency: items[0]?.currency,
dataSource: this.getName(),
name: items[0]?.name
}; };
} }
@ -122,32 +124,30 @@ export class EodHistoricalDataService implements DataProviderInterface {
200 200
); );
const [response, symbolProfiles] = await Promise.all([ const [realTimeResponse, searchResponse] = await Promise.all([
get(), get(),
this.symbolProfileService.getSymbolProfiles( this.search(aSymbols[0])
aSymbols.map((symbol) => {
return {
symbol,
dataSource: DataSource.EOD_HISTORICAL_DATA
};
})
)
]); ]);
const quotes = aSymbols.length === 1 ? [response] : response; const quotes =
aSymbols.length === 1 ? [realTimeResponse] : realTimeResponse;
return quotes.reduce((result, item, index, array) => {
result[item.code] = { return quotes.reduce(
currency: symbolProfiles.find((symbolProfile) => { (
return symbolProfile.symbol === item.code; result: { [symbol: string]: IDataProviderResponse },
})?.currency, { close, code, timestamp }
dataSource: DataSource.EOD_HISTORICAL_DATA, ) => {
marketPrice: item.close, result[code] = {
marketState: 'delayed' currency: searchResponse?.items[0]?.currency,
}; dataSource: DataSource.EOD_HISTORICAL_DATA,
marketPrice: close,
marketState: isToday(new Date(timestamp * 1000)) ? 'open' : 'closed'
};
return result; return result;
}, {}); },
{}
);
} catch (error) { } catch (error) {
Logger.error(error, 'EodHistoricalDataService'); Logger.error(error, 'EodHistoricalDataService');
} }
@ -156,6 +156,35 @@ export class EodHistoricalDataService implements DataProviderInterface {
} }
public async search(aQuery: string): Promise<{ items: LookupItem[] }> { public async search(aQuery: string): Promise<{ items: LookupItem[] }> {
return { items: [] }; let items: LookupItem[] = [];
if (aQuery.length <= 2) {
return { items };
}
try {
const get = bent(
`${this.URL}/search/${aQuery}?api_token=${this.apiKey}`,
'GET',
'json',
200
);
const response = await get();
items = response.map(
({ Code, Currency: currency, Exchange, Name: name }) => {
return {
currency,
name,
dataSource: this.getName(),
symbol: `${Code}.${Exchange}`
};
}
);
} catch (error) {
Logger.error(error, 'EodHistoricalDataService');
}
return { items };
} }
} }

@ -61,7 +61,8 @@
<span><b>{{ lookupItem.name }}</b></span> <span><b>{{ lookupItem.name }}</b></span>
<br /> <br />
<small class="text-muted" <small class="text-muted"
>{{ lookupItem.symbol | gfSymbol }}</small >{{ lookupItem.symbol | gfSymbol }} · {{ lookupItem.currency
}}</small
> >
</mat-option> </mat-option>
</ng-container> </ng-container>

Loading…
Cancel
Save