Feature/add search functionality for eod historical data (#1783)
* Add search functionality for EOD_HISTORICAL_DATA * Update changelog
This commit is contained in:
parent
e37a34ed6c
commit
7c6ff776d9
@ -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) => {
|
return quotes.reduce(
|
||||||
result[item.code] = {
|
(
|
||||||
currency: symbolProfiles.find((symbolProfile) => {
|
result: { [symbol: string]: IDataProviderResponse },
|
||||||
return symbolProfile.symbol === item.code;
|
{ close, code, timestamp }
|
||||||
})?.currency,
|
) => {
|
||||||
dataSource: DataSource.EOD_HISTORICAL_DATA,
|
result[code] = {
|
||||||
marketPrice: item.close,
|
currency: searchResponse?.items[0]?.currency,
|
||||||
marketState: 'delayed'
|
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…
x
Reference in New Issue
Block a user