|
|
@ -1,6 +1,6 @@
|
|
|
|
|
|
|
|
import { PortfolioService } from '@ghostfolio/api/app/portfolio/portfolio.service';
|
|
|
|
import { RedisCacheService } from '@ghostfolio/api/app/redis-cache/redis-cache.service';
|
|
|
|
import { RedisCacheService } from '@ghostfolio/api/app/redis-cache/redis-cache.service';
|
|
|
|
import { SymbolService } from '@ghostfolio/api/app/symbol/symbol.service';
|
|
|
|
import { SymbolService } from '@ghostfolio/api/app/symbol/symbol.service';
|
|
|
|
import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service';
|
|
|
|
|
|
|
|
import { DataProviderService } from '@ghostfolio/api/services/data-provider/data-provider.service';
|
|
|
|
import { DataProviderService } from '@ghostfolio/api/services/data-provider/data-provider.service';
|
|
|
|
import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service';
|
|
|
|
import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service';
|
|
|
|
import { MarketDataService } from '@ghostfolio/api/services/market-data/market-data.service';
|
|
|
|
import { MarketDataService } from '@ghostfolio/api/services/market-data/market-data.service';
|
|
|
@ -22,22 +22,19 @@ import {
|
|
|
|
Benchmark,
|
|
|
|
Benchmark,
|
|
|
|
BenchmarkMarketDataDetails,
|
|
|
|
BenchmarkMarketDataDetails,
|
|
|
|
BenchmarkProperty,
|
|
|
|
BenchmarkProperty,
|
|
|
|
BenchmarkResponse
|
|
|
|
BenchmarkResponse,
|
|
|
|
|
|
|
|
Filter
|
|
|
|
} from '@ghostfolio/common/interfaces';
|
|
|
|
} from '@ghostfolio/common/interfaces';
|
|
|
|
import { BenchmarkTrend } from '@ghostfolio/common/types';
|
|
|
|
import {
|
|
|
|
|
|
|
|
BenchmarkTrend,
|
|
|
|
|
|
|
|
DateRange,
|
|
|
|
|
|
|
|
UserWithSettings
|
|
|
|
|
|
|
|
} from '@ghostfolio/common/types';
|
|
|
|
|
|
|
|
|
|
|
|
import { Injectable, Logger } from '@nestjs/common';
|
|
|
|
import { Injectable, Logger } from '@nestjs/common';
|
|
|
|
import { SymbolProfile } from '@prisma/client';
|
|
|
|
import { SymbolProfile } from '@prisma/client';
|
|
|
|
import { Big } from 'big.js';
|
|
|
|
import { Big } from 'big.js';
|
|
|
|
import {
|
|
|
|
import { addHours, format, isAfter, isSameDay, subDays } from 'date-fns';
|
|
|
|
addHours,
|
|
|
|
|
|
|
|
differenceInDays,
|
|
|
|
|
|
|
|
eachDayOfInterval,
|
|
|
|
|
|
|
|
format,
|
|
|
|
|
|
|
|
isAfter,
|
|
|
|
|
|
|
|
isSameDay,
|
|
|
|
|
|
|
|
subDays
|
|
|
|
|
|
|
|
} from 'date-fns';
|
|
|
|
|
|
|
|
import { isNumber, uniqBy } from 'lodash';
|
|
|
|
import { isNumber, uniqBy } from 'lodash';
|
|
|
|
import ms from 'ms';
|
|
|
|
import ms from 'ms';
|
|
|
|
|
|
|
|
|
|
|
@ -48,11 +45,11 @@ export class BenchmarkService {
|
|
|
|
private readonly CACHE_KEY_BENCHMARKS = 'BENCHMARKS';
|
|
|
|
private readonly CACHE_KEY_BENCHMARKS = 'BENCHMARKS';
|
|
|
|
|
|
|
|
|
|
|
|
public constructor(
|
|
|
|
public constructor(
|
|
|
|
private readonly configurationService: ConfigurationService,
|
|
|
|
|
|
|
|
private readonly dataProviderService: DataProviderService,
|
|
|
|
private readonly dataProviderService: DataProviderService,
|
|
|
|
private readonly exchangeRateDataService: ExchangeRateDataService,
|
|
|
|
private readonly exchangeRateDataService: ExchangeRateDataService,
|
|
|
|
private readonly marketDataService: MarketDataService,
|
|
|
|
private readonly marketDataService: MarketDataService,
|
|
|
|
private readonly prismaService: PrismaService,
|
|
|
|
private readonly prismaService: PrismaService,
|
|
|
|
|
|
|
|
private readonly portfolioService: PortfolioService,
|
|
|
|
private readonly propertyService: PropertyService,
|
|
|
|
private readonly propertyService: PropertyService,
|
|
|
|
private readonly redisCacheService: RedisCacheService,
|
|
|
|
private readonly redisCacheService: RedisCacheService,
|
|
|
|
private readonly symbolProfileService: SymbolProfileService,
|
|
|
|
private readonly symbolProfileService: SymbolProfileService,
|
|
|
@ -158,31 +155,33 @@ export class BenchmarkService {
|
|
|
|
|
|
|
|
|
|
|
|
public async getMarketDataForUser({
|
|
|
|
public async getMarketDataForUser({
|
|
|
|
dataSource,
|
|
|
|
dataSource,
|
|
|
|
|
|
|
|
dateRange,
|
|
|
|
endDate = new Date(),
|
|
|
|
endDate = new Date(),
|
|
|
|
|
|
|
|
filters,
|
|
|
|
|
|
|
|
impersonationId,
|
|
|
|
startDate,
|
|
|
|
startDate,
|
|
|
|
symbol,
|
|
|
|
symbol,
|
|
|
|
userCurrency
|
|
|
|
user,
|
|
|
|
|
|
|
|
withExcludedAccounts
|
|
|
|
}: {
|
|
|
|
}: {
|
|
|
|
|
|
|
|
dateRange: DateRange;
|
|
|
|
endDate?: Date;
|
|
|
|
endDate?: Date;
|
|
|
|
|
|
|
|
filters?: Filter[];
|
|
|
|
|
|
|
|
impersonationId: string;
|
|
|
|
startDate: Date;
|
|
|
|
startDate: Date;
|
|
|
|
userCurrency: string;
|
|
|
|
user: UserWithSettings;
|
|
|
|
|
|
|
|
withExcludedAccounts?: boolean;
|
|
|
|
} & AssetProfileIdentifier): Promise<BenchmarkMarketDataDetails> {
|
|
|
|
} & AssetProfileIdentifier): Promise<BenchmarkMarketDataDetails> {
|
|
|
|
const marketData: { date: string; value: number }[] = [];
|
|
|
|
const marketData: { date: string; value: number }[] = [];
|
|
|
|
|
|
|
|
const userCurrency = user.Settings.settings.baseCurrency;
|
|
|
|
const days = differenceInDays(endDate, startDate) + 1;
|
|
|
|
const userId = user.id;
|
|
|
|
const dates = eachDayOfInterval(
|
|
|
|
|
|
|
|
{
|
|
|
|
const { chart } = await this.portfolioService.getPerformance({
|
|
|
|
start: startDate,
|
|
|
|
dateRange,
|
|
|
|
end: endDate
|
|
|
|
filters,
|
|
|
|
},
|
|
|
|
impersonationId,
|
|
|
|
{
|
|
|
|
userId,
|
|
|
|
step: Math.round(
|
|
|
|
withExcludedAccounts
|
|
|
|
days /
|
|
|
|
|
|
|
|
Math.min(days, this.configurationService.get('MAX_CHART_ITEMS'))
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
).map((date) => {
|
|
|
|
|
|
|
|
return resetHours(date);
|
|
|
|
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
const [currentSymbolItem, marketDataItems] = await Promise.all([
|
|
|
|
const [currentSymbolItem, marketDataItems] = await Promise.all([
|
|
|
@ -200,7 +199,9 @@ export class BenchmarkService {
|
|
|
|
dataSource,
|
|
|
|
dataSource,
|
|
|
|
symbol,
|
|
|
|
symbol,
|
|
|
|
date: {
|
|
|
|
date: {
|
|
|
|
in: dates
|
|
|
|
in: chart.map(({ date }) => {
|
|
|
|
|
|
|
|
return resetHours(parseDate(date));
|
|
|
|
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
})
|
|
|
|