mirror of https://github.com/ghostfolio/ghostfolio
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
155 lines
4.4 KiB
155 lines
4.4 KiB
import { BenchmarkService } from '@ghostfolio/api/services/benchmark/benchmark.service';
|
|
import { DataProviderService } from '@ghostfolio/api/services/data-provider/data-provider.service';
|
|
import { MarketDataService } from '@ghostfolio/api/services/market-data/market-data.service';
|
|
import { PrismaService } from '@ghostfolio/api/services/prisma/prisma.service';
|
|
import { DataGatheringService } from '@ghostfolio/api/services/queues/data-gathering/data-gathering.service';
|
|
import { SymbolProfileService } from '@ghostfolio/api/services/symbol-profile/symbol-profile.service';
|
|
import { WatchlistResponse } from '@ghostfolio/common/interfaces';
|
|
|
|
import { BadRequestException, Injectable } from '@nestjs/common';
|
|
import { DataSource, Prisma } from '@prisma/client';
|
|
|
|
@Injectable()
|
|
export class WatchlistService {
|
|
public constructor(
|
|
private readonly benchmarkService: BenchmarkService,
|
|
private readonly dataGatheringService: DataGatheringService,
|
|
private readonly dataProviderService: DataProviderService,
|
|
private readonly marketDataService: MarketDataService,
|
|
private readonly prismaService: PrismaService,
|
|
private readonly symbolProfileService: SymbolProfileService
|
|
) {}
|
|
|
|
public async createWatchlistItem({
|
|
dataSource,
|
|
symbol,
|
|
userId
|
|
}: {
|
|
dataSource: DataSource;
|
|
symbol: string;
|
|
userId: string;
|
|
}): Promise<void> {
|
|
const symbolProfile = await this.prismaService.symbolProfile.findUnique({
|
|
where: {
|
|
dataSource_symbol: { dataSource, symbol }
|
|
}
|
|
});
|
|
|
|
if (!symbolProfile) {
|
|
const assetProfiles = await this.dataProviderService.getAssetProfiles([
|
|
{ dataSource, symbol }
|
|
]);
|
|
|
|
if (!assetProfiles[symbol]?.currency) {
|
|
throw new BadRequestException(
|
|
`Asset profile not found for ${symbol} (${dataSource})`
|
|
);
|
|
}
|
|
|
|
await this.symbolProfileService.add(
|
|
assetProfiles[symbol] as Prisma.SymbolProfileCreateInput
|
|
);
|
|
}
|
|
|
|
await this.dataGatheringService.gatherSymbol({
|
|
dataSource,
|
|
symbol
|
|
});
|
|
|
|
await this.prismaService.user.update({
|
|
data: {
|
|
watchlist: {
|
|
connect: {
|
|
dataSource_symbol: { dataSource, symbol }
|
|
}
|
|
}
|
|
},
|
|
where: { id: userId }
|
|
});
|
|
}
|
|
|
|
public async deleteWatchlistItem({
|
|
dataSource,
|
|
symbol,
|
|
userId
|
|
}: {
|
|
dataSource: DataSource;
|
|
symbol: string;
|
|
userId: string;
|
|
}) {
|
|
await this.prismaService.user.update({
|
|
data: {
|
|
watchlist: {
|
|
disconnect: {
|
|
dataSource_symbol: { dataSource, symbol }
|
|
}
|
|
}
|
|
},
|
|
where: { id: userId }
|
|
});
|
|
}
|
|
|
|
public async getWatchlistItems(
|
|
userId: string
|
|
): Promise<WatchlistResponse['watchlist']> {
|
|
const user = await this.prismaService.user.findUnique({
|
|
select: {
|
|
watchlist: {
|
|
select: { dataSource: true, symbol: true }
|
|
}
|
|
},
|
|
where: { id: userId }
|
|
});
|
|
|
|
const [assetProfiles, quotes] = await Promise.all([
|
|
this.symbolProfileService.getSymbolProfiles(user.watchlist),
|
|
this.dataProviderService.getQuotes({
|
|
items: user.watchlist.map(({ dataSource, symbol }) => {
|
|
return { dataSource, symbol };
|
|
})
|
|
})
|
|
]);
|
|
|
|
const watchlist = await Promise.all(
|
|
user.watchlist.map(async ({ dataSource, symbol }) => {
|
|
const assetProfile = assetProfiles.find((profile) => {
|
|
return profile.dataSource === dataSource && profile.symbol === symbol;
|
|
});
|
|
|
|
const [allTimeHigh, trends] = await Promise.all([
|
|
this.marketDataService.getMax({
|
|
dataSource,
|
|
symbol
|
|
}),
|
|
this.benchmarkService.getBenchmarkTrends({ dataSource, symbol })
|
|
]);
|
|
|
|
const performancePercent =
|
|
this.benchmarkService.calculateChangeInPercentage(
|
|
allTimeHigh?.marketPrice,
|
|
quotes[symbol]?.marketPrice
|
|
);
|
|
|
|
return {
|
|
dataSource,
|
|
symbol,
|
|
marketCondition:
|
|
this.benchmarkService.getMarketCondition(performancePercent),
|
|
name: assetProfile?.name,
|
|
performances: {
|
|
allTimeHigh: {
|
|
performancePercent,
|
|
date: allTimeHigh?.date
|
|
}
|
|
},
|
|
trend50d: trends.trend50d,
|
|
trend200d: trends.trend200d
|
|
};
|
|
})
|
|
);
|
|
|
|
return watchlist.sort((a, b) => {
|
|
return a.name.localeCompare(b.name);
|
|
});
|
|
}
|
|
}
|
|
|