|
|
|
@ -5,11 +5,13 @@ import {
|
|
|
|
|
IDataProviderHistoricalResponse,
|
|
|
|
|
IDataProviderResponse
|
|
|
|
|
} from '@ghostfolio/api/services/interfaces/interfaces';
|
|
|
|
|
import { DATE_FORMAT, parseDate } from '@ghostfolio/common/helper';
|
|
|
|
|
import { DataProviderInfo } from '@ghostfolio/common/interfaces';
|
|
|
|
|
import { Granularity } from '@ghostfolio/common/types';
|
|
|
|
|
import { Injectable, Logger } from '@nestjs/common';
|
|
|
|
|
import { DataSource, SymbolProfile } from '@prisma/client';
|
|
|
|
|
import bent from 'bent';
|
|
|
|
|
import { format, isAfter, isBefore, isSameDay } from 'date-fns';
|
|
|
|
|
|
|
|
|
|
@Injectable()
|
|
|
|
|
export class FinancialModelingPrepService implements DataProviderInterface {
|
|
|
|
@ -61,9 +63,42 @@ export class FinancialModelingPrepService implements DataProviderInterface {
|
|
|
|
|
): Promise<{
|
|
|
|
|
[symbol: string]: { [date: string]: IDataProviderHistoricalResponse };
|
|
|
|
|
}> {
|
|
|
|
|
return {
|
|
|
|
|
[aSymbol]: {}
|
|
|
|
|
};
|
|
|
|
|
try {
|
|
|
|
|
const get = bent(
|
|
|
|
|
`${this.URL}/historical-price-full/${aSymbol}?apikey=${this.apiKey}`,
|
|
|
|
|
'GET',
|
|
|
|
|
'json',
|
|
|
|
|
200
|
|
|
|
|
);
|
|
|
|
|
const { historical } = await get();
|
|
|
|
|
|
|
|
|
|
const result: {
|
|
|
|
|
[symbol: string]: { [date: string]: IDataProviderHistoricalResponse };
|
|
|
|
|
} = {
|
|
|
|
|
[aSymbol]: {}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
for (const { close, date } of historical) {
|
|
|
|
|
if (
|
|
|
|
|
(isSameDay(parseDate(date), from) ||
|
|
|
|
|
isAfter(parseDate(date), from)) &&
|
|
|
|
|
isBefore(parseDate(date), to)
|
|
|
|
|
) {
|
|
|
|
|
result[aSymbol][date] = {
|
|
|
|
|
marketPrice: close
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
} catch (error) {
|
|
|
|
|
throw new Error(
|
|
|
|
|
`Could not get historical market data for ${aSymbol} (${this.getName()}) from ${format(
|
|
|
|
|
from,
|
|
|
|
|
DATE_FORMAT
|
|
|
|
|
)} to ${format(to, DATE_FORMAT)}: [${error.name}] ${error.message}`
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public getName(): DataSource {
|
|
|
|
@ -109,7 +144,32 @@ export class FinancialModelingPrepService implements DataProviderInterface {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async search(aQuery: string): Promise<{ items: LookupItem[] }> {
|
|
|
|
|
return { items: [] };
|
|
|
|
|
let items: LookupItem[] = [];
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
const get = bent(
|
|
|
|
|
`${this.URL}/search?query=${aQuery}&apikey=${this.apiKey}`,
|
|
|
|
|
'GET',
|
|
|
|
|
'json',
|
|
|
|
|
200
|
|
|
|
|
);
|
|
|
|
|
const result = await get();
|
|
|
|
|
|
|
|
|
|
items = result.map(({ currency, name, symbol }) => {
|
|
|
|
|
return {
|
|
|
|
|
// TODO: Add assetClass
|
|
|
|
|
// TODO: Add assetSubClass
|
|
|
|
|
currency,
|
|
|
|
|
name,
|
|
|
|
|
symbol,
|
|
|
|
|
dataSource: this.getName()
|
|
|
|
|
};
|
|
|
|
|
});
|
|
|
|
|
} catch (error) {
|
|
|
|
|
Logger.error(error, 'FinancialModelingPrepService');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return { items };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private getDataProviderInfo(): DataProviderInfo {
|
|
|
|
|