|
|
@ -3,7 +3,6 @@ import { LookupItem } from '@ghostfolio/api/app/symbol/interfaces/lookup-item.in |
|
|
|
import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; |
|
|
|
import { DataProviderInterface } from '@ghostfolio/api/services/data-provider/interfaces/data-provider.interface'; |
|
|
|
import { |
|
|
|
IDataGatheringItem, |
|
|
|
IDataProviderHistoricalResponse, |
|
|
|
IDataProviderResponse |
|
|
|
} from '@ghostfolio/api/services/interfaces/interfaces'; |
|
|
@ -12,6 +11,7 @@ import { PrismaService } from '@ghostfolio/api/services/prisma/prisma.service'; |
|
|
|
import { PropertyService } from '@ghostfolio/api/services/property/property.service'; |
|
|
|
import { PROPERTY_DATA_SOURCE_MAPPING } from '@ghostfolio/common/config'; |
|
|
|
import { DATE_FORMAT, getStartOfUtcDate } from '@ghostfolio/common/helper'; |
|
|
|
import { UniqueAsset } from '@ghostfolio/common/interfaces'; |
|
|
|
import type { Granularity, UserWithSettings } from '@ghostfolio/common/types'; |
|
|
|
import { Inject, Injectable, Logger } from '@nestjs/common'; |
|
|
|
import { DataSource, MarketData, SymbolProfile } from '@prisma/client'; |
|
|
@ -45,12 +45,15 @@ export class DataProviderService { |
|
|
|
const dataProvider = this.getDataProvider(dataSource); |
|
|
|
const symbol = dataProvider.getTestSymbol(); |
|
|
|
|
|
|
|
const quotes = await this.getQuotes([ |
|
|
|
const quotes = await this.getQuotes({ |
|
|
|
items: [ |
|
|
|
{ |
|
|
|
dataSource, |
|
|
|
symbol |
|
|
|
} |
|
|
|
]); |
|
|
|
], |
|
|
|
useCache: false |
|
|
|
}); |
|
|
|
|
|
|
|
if (quotes[symbol]?.marketPrice > 0) { |
|
|
|
return true; |
|
|
@ -59,14 +62,16 @@ export class DataProviderService { |
|
|
|
return false; |
|
|
|
} |
|
|
|
|
|
|
|
public async getAssetProfiles(items: IDataGatheringItem[]): Promise<{ |
|
|
|
public async getAssetProfiles(items: UniqueAsset[]): Promise<{ |
|
|
|
[symbol: string]: Partial<SymbolProfile>; |
|
|
|
}> { |
|
|
|
const response: { |
|
|
|
[symbol: string]: Partial<SymbolProfile>; |
|
|
|
} = {}; |
|
|
|
|
|
|
|
const itemsGroupedByDataSource = groupBy(items, (item) => item.dataSource); |
|
|
|
const itemsGroupedByDataSource = groupBy(items, ({ dataSource }) => { |
|
|
|
return dataSource; |
|
|
|
}); |
|
|
|
|
|
|
|
const promises = []; |
|
|
|
|
|
|
@ -127,7 +132,7 @@ export class DataProviderService { |
|
|
|
} |
|
|
|
|
|
|
|
public async getHistorical( |
|
|
|
aItems: IDataGatheringItem[], |
|
|
|
aItems: UniqueAsset[], |
|
|
|
aGranularity: Granularity = 'month', |
|
|
|
from: Date, |
|
|
|
to: Date |
|
|
@ -155,11 +160,11 @@ export class DataProviderService { |
|
|
|
)}'` |
|
|
|
: ''; |
|
|
|
|
|
|
|
const dataSources = aItems.map((item) => { |
|
|
|
return item.dataSource; |
|
|
|
const dataSources = aItems.map(({ dataSource }) => { |
|
|
|
return dataSource; |
|
|
|
}); |
|
|
|
const symbols = aItems.map((item) => { |
|
|
|
return item.symbol; |
|
|
|
const symbols = aItems.map(({ symbol }) => { |
|
|
|
return symbol; |
|
|
|
}); |
|
|
|
|
|
|
|
try { |
|
|
@ -192,7 +197,7 @@ export class DataProviderService { |
|
|
|
} |
|
|
|
|
|
|
|
public async getHistoricalRaw( |
|
|
|
aDataGatheringItems: IDataGatheringItem[], |
|
|
|
aDataGatheringItems: UniqueAsset[], |
|
|
|
from: Date, |
|
|
|
to: Date |
|
|
|
): Promise<{ |
|
|
@ -229,7 +234,13 @@ export class DataProviderService { |
|
|
|
return result; |
|
|
|
} |
|
|
|
|
|
|
|
public async getQuotes(items: IDataGatheringItem[]): Promise<{ |
|
|
|
public async getQuotes({ |
|
|
|
items, |
|
|
|
useCache = true |
|
|
|
}: { |
|
|
|
items: UniqueAsset[]; |
|
|
|
useCache?: boolean; |
|
|
|
}): Promise<{ |
|
|
|
[symbol: string]: IDataProviderResponse; |
|
|
|
}> { |
|
|
|
const response: { |
|
|
@ -238,9 +249,10 @@ export class DataProviderService { |
|
|
|
const startTimeTotal = performance.now(); |
|
|
|
|
|
|
|
// Get items from cache
|
|
|
|
const itemsToFetch: IDataGatheringItem[] = []; |
|
|
|
const itemsToFetch: UniqueAsset[] = []; |
|
|
|
|
|
|
|
for (const { dataSource, symbol } of items) { |
|
|
|
if (useCache) { |
|
|
|
const quoteString = await this.redisCacheService.get( |
|
|
|
this.redisCacheService.getQuoteKey({ dataSource, symbol }) |
|
|
|
); |
|
|
@ -249,13 +261,13 @@ export class DataProviderService { |
|
|
|
try { |
|
|
|
const cachedDataProviderResponse = JSON.parse(quoteString); |
|
|
|
response[symbol] = cachedDataProviderResponse; |
|
|
|
continue; |
|
|
|
} catch {} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
if (!quoteString) { |
|
|
|
itemsToFetch.push({ dataSource, symbol }); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
const numberOfItemsInCache = Object.keys(response)?.length; |
|
|
|
|
|
|
|