Feature/refactor unique asset type to asset profile identifier (#3636)
* Refactoring
This commit is contained in:
parent
4c7657a90e
commit
41f5801b5e
@ -21,9 +21,9 @@ import {
|
||||
AdminMarketData,
|
||||
AdminMarketDataDetails,
|
||||
AdminMarketDataItem,
|
||||
AssetProfileIdentifier,
|
||||
EnhancedSymbolProfile,
|
||||
Filter,
|
||||
UniqueAsset
|
||||
Filter
|
||||
} from '@ghostfolio/common/interfaces';
|
||||
import { MarketDataPreset } from '@ghostfolio/common/types';
|
||||
|
||||
@ -59,7 +59,9 @@ export class AdminService {
|
||||
currency,
|
||||
dataSource,
|
||||
symbol
|
||||
}: UniqueAsset & { currency?: string }): Promise<SymbolProfile | never> {
|
||||
}: AssetProfileIdentifier & { currency?: string }): Promise<
|
||||
SymbolProfile | never
|
||||
> {
|
||||
try {
|
||||
if (dataSource === 'MANUAL') {
|
||||
return this.symbolProfileService.add({
|
||||
@ -96,7 +98,10 @@ export class AdminService {
|
||||
}
|
||||
}
|
||||
|
||||
public async deleteProfileData({ dataSource, symbol }: UniqueAsset) {
|
||||
public async deleteProfileData({
|
||||
dataSource,
|
||||
symbol
|
||||
}: AssetProfileIdentifier) {
|
||||
await this.marketDataService.deleteMany({ dataSource, symbol });
|
||||
await this.symbolProfileService.delete({ dataSource, symbol });
|
||||
}
|
||||
@ -325,7 +330,7 @@ export class AdminService {
|
||||
public async getMarketDataBySymbol({
|
||||
dataSource,
|
||||
symbol
|
||||
}: UniqueAsset): Promise<AdminMarketDataDetails> {
|
||||
}: AssetProfileIdentifier): Promise<AdminMarketDataDetails> {
|
||||
let activitiesCount: EnhancedSymbolProfile['activitiesCount'] = 0;
|
||||
let currency: EnhancedSymbolProfile['currency'] = '-';
|
||||
let dateOfFirstActivity: EnhancedSymbolProfile['dateOfFirstActivity'];
|
||||
@ -386,7 +391,7 @@ export class AdminService {
|
||||
symbol,
|
||||
symbolMapping,
|
||||
url
|
||||
}: Prisma.SymbolProfileUpdateInput & UniqueAsset) {
|
||||
}: AssetProfileIdentifier & Prisma.SymbolProfileUpdateInput) {
|
||||
const symbolProfileOverrides = {
|
||||
assetClass: assetClass as AssetClass,
|
||||
assetSubClass: assetSubClass as AssetSubClass,
|
||||
@ -394,28 +399,28 @@ export class AdminService {
|
||||
url: url as string
|
||||
};
|
||||
|
||||
const updatedSymbolProfile: Prisma.SymbolProfileUpdateInput & UniqueAsset =
|
||||
{
|
||||
comment,
|
||||
countries,
|
||||
currency,
|
||||
dataSource,
|
||||
holdings,
|
||||
scraperConfiguration,
|
||||
sectors,
|
||||
symbol,
|
||||
symbolMapping,
|
||||
...(dataSource === 'MANUAL'
|
||||
? { assetClass, assetSubClass, name, url }
|
||||
: {
|
||||
SymbolProfileOverrides: {
|
||||
upsert: {
|
||||
create: symbolProfileOverrides,
|
||||
update: symbolProfileOverrides
|
||||
}
|
||||
const updatedSymbolProfile: AssetProfileIdentifier &
|
||||
Prisma.SymbolProfileUpdateInput = {
|
||||
comment,
|
||||
countries,
|
||||
currency,
|
||||
dataSource,
|
||||
holdings,
|
||||
scraperConfiguration,
|
||||
sectors,
|
||||
symbol,
|
||||
symbolMapping,
|
||||
...(dataSource === 'MANUAL'
|
||||
? { assetClass, assetSubClass, name, url }
|
||||
: {
|
||||
SymbolProfileOverrides: {
|
||||
upsert: {
|
||||
create: symbolProfileOverrides,
|
||||
update: symbolProfileOverrides
|
||||
}
|
||||
})
|
||||
};
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
await this.symbolProfileService.updateSymbolProfile(updatedSymbolProfile);
|
||||
|
||||
|
@ -4,9 +4,9 @@ import { getInterval } from '@ghostfolio/api/helper/portfolio.helper';
|
||||
import { TransformDataSourceInRequestInterceptor } from '@ghostfolio/api/interceptors/transform-data-source-in-request/transform-data-source-in-request.interceptor';
|
||||
import { TransformDataSourceInResponseInterceptor } from '@ghostfolio/api/interceptors/transform-data-source-in-response/transform-data-source-in-response.interceptor';
|
||||
import type {
|
||||
AssetProfileIdentifier,
|
||||
BenchmarkMarketDataDetails,
|
||||
BenchmarkResponse,
|
||||
UniqueAsset
|
||||
BenchmarkResponse
|
||||
} from '@ghostfolio/common/interfaces';
|
||||
import { permissions } from '@ghostfolio/common/permissions';
|
||||
import type { DateRange, RequestWithUser } from '@ghostfolio/common/types';
|
||||
@ -41,7 +41,9 @@ export class BenchmarkController {
|
||||
@HasPermission(permissions.accessAdminControl)
|
||||
@Post()
|
||||
@UseGuards(AuthGuard('jwt'), HasPermissionGuard)
|
||||
public async addBenchmark(@Body() { dataSource, symbol }: UniqueAsset) {
|
||||
public async addBenchmark(
|
||||
@Body() { dataSource, symbol }: AssetProfileIdentifier
|
||||
) {
|
||||
try {
|
||||
const benchmark = await this.benchmarkService.addBenchmark({
|
||||
dataSource,
|
||||
|
@ -17,11 +17,11 @@ import {
|
||||
resetHours
|
||||
} from '@ghostfolio/common/helper';
|
||||
import {
|
||||
AssetProfileIdentifier,
|
||||
Benchmark,
|
||||
BenchmarkMarketDataDetails,
|
||||
BenchmarkProperty,
|
||||
BenchmarkResponse,
|
||||
UniqueAsset
|
||||
BenchmarkResponse
|
||||
} from '@ghostfolio/common/interfaces';
|
||||
import { BenchmarkTrend } from '@ghostfolio/common/types';
|
||||
|
||||
@ -61,7 +61,10 @@ export class BenchmarkService {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public async getBenchmarkTrends({ dataSource, symbol }: UniqueAsset) {
|
||||
public async getBenchmarkTrends({
|
||||
dataSource,
|
||||
symbol
|
||||
}: AssetProfileIdentifier) {
|
||||
const historicalData = await this.marketDataService.marketDataItems({
|
||||
orderBy: {
|
||||
date: 'desc'
|
||||
@ -228,7 +231,7 @@ export class BenchmarkService {
|
||||
endDate?: Date;
|
||||
startDate: Date;
|
||||
userCurrency: string;
|
||||
} & UniqueAsset): Promise<BenchmarkMarketDataDetails> {
|
||||
} & AssetProfileIdentifier): Promise<BenchmarkMarketDataDetails> {
|
||||
const marketData: { date: string; value: number }[] = [];
|
||||
|
||||
const days = differenceInDays(endDate, startDate) + 1;
|
||||
@ -348,7 +351,7 @@ export class BenchmarkService {
|
||||
public async addBenchmark({
|
||||
dataSource,
|
||||
symbol
|
||||
}: UniqueAsset): Promise<Partial<SymbolProfile>> {
|
||||
}: AssetProfileIdentifier): Promise<Partial<SymbolProfile>> {
|
||||
const assetProfile = await this.prismaService.symbolProfile.findFirst({
|
||||
where: {
|
||||
dataSource,
|
||||
@ -385,7 +388,7 @@ export class BenchmarkService {
|
||||
public async deleteBenchmark({
|
||||
dataSource,
|
||||
symbol
|
||||
}: UniqueAsset): Promise<Partial<SymbolProfile>> {
|
||||
}: AssetProfileIdentifier): Promise<Partial<SymbolProfile>> {
|
||||
const assetProfile = await this.prismaService.symbolProfile.findFirst({
|
||||
where: {
|
||||
dataSource,
|
||||
|
@ -19,7 +19,7 @@ import {
|
||||
getAssetProfileIdentifier,
|
||||
parseDate
|
||||
} from '@ghostfolio/common/helper';
|
||||
import { UniqueAsset } from '@ghostfolio/common/interfaces';
|
||||
import { AssetProfileIdentifier } from '@ghostfolio/common/interfaces';
|
||||
import {
|
||||
AccountWithPlatform,
|
||||
OrderWithAccount,
|
||||
@ -51,7 +51,7 @@ export class ImportService {
|
||||
dataSource,
|
||||
symbol,
|
||||
userCurrency
|
||||
}: UniqueAsset & { userCurrency: string }): Promise<Activity[]> {
|
||||
}: AssetProfileIdentifier & { userCurrency: string }): Promise<Activity[]> {
|
||||
try {
|
||||
const { firstBuyDate, historicalData, orders } =
|
||||
await this.portfolioService.getPosition(dataSource, undefined, symbol);
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service';
|
||||
import { SymbolProfileService } from '@ghostfolio/api/services/symbol-profile/symbol-profile.service';
|
||||
import { UniqueAsset } from '@ghostfolio/common/interfaces';
|
||||
import { AssetProfileIdentifier } from '@ghostfolio/common/interfaces';
|
||||
|
||||
import { HttpException, Injectable } from '@nestjs/common';
|
||||
import { DataSource } from '@prisma/client';
|
||||
@ -17,7 +17,7 @@ export class LogoService {
|
||||
public async getLogoByDataSourceAndSymbol({
|
||||
dataSource,
|
||||
symbol
|
||||
}: UniqueAsset) {
|
||||
}: AssetProfileIdentifier) {
|
||||
if (!DataSource[dataSource]) {
|
||||
throw new HttpException(
|
||||
getReasonPhrase(StatusCodes.NOT_FOUND),
|
||||
|
@ -11,9 +11,9 @@ import {
|
||||
} from '@ghostfolio/common/config';
|
||||
import { getAssetProfileIdentifier } from '@ghostfolio/common/helper';
|
||||
import {
|
||||
AssetProfileIdentifier,
|
||||
EnhancedSymbolProfile,
|
||||
Filter,
|
||||
UniqueAsset
|
||||
Filter
|
||||
} from '@ghostfolio/common/interfaces';
|
||||
import { OrderWithAccount } from '@ghostfolio/common/types';
|
||||
|
||||
@ -51,7 +51,7 @@ export class OrderService {
|
||||
symbol,
|
||||
tags,
|
||||
userId
|
||||
}: { tags: Tag[]; userId: string } & UniqueAsset) {
|
||||
}: { tags: Tag[]; userId: string } & AssetProfileIdentifier) {
|
||||
const orders = await this.prismaService.order.findMany({
|
||||
where: {
|
||||
userId,
|
||||
@ -285,7 +285,7 @@ export class OrderService {
|
||||
return count;
|
||||
}
|
||||
|
||||
public async getLatestOrder({ dataSource, symbol }: UniqueAsset) {
|
||||
public async getLatestOrder({ dataSource, symbol }: AssetProfileIdentifier) {
|
||||
return this.prismaService.order.findFirst({
|
||||
orderBy: {
|
||||
date: 'desc'
|
||||
@ -464,7 +464,7 @@ export class OrderService {
|
||||
this.prismaService.order.count({ where })
|
||||
]);
|
||||
|
||||
const uniqueAssets = uniqBy(
|
||||
const assetProfileIdentifiers = uniqBy(
|
||||
orders.map(({ SymbolProfile }) => {
|
||||
return {
|
||||
dataSource: SymbolProfile.dataSource,
|
||||
@ -479,8 +479,9 @@ export class OrderService {
|
||||
}
|
||||
);
|
||||
|
||||
const assetProfiles =
|
||||
await this.symbolProfileService.getSymbolProfiles(uniqueAssets);
|
||||
const assetProfiles = await this.symbolProfileService.getSymbolProfiles(
|
||||
assetProfileIdentifiers
|
||||
);
|
||||
|
||||
const activities = orders.map((order) => {
|
||||
const assetProfile = assetProfiles.find(({ dataSource, symbol }) => {
|
||||
|
@ -1,5 +1,8 @@
|
||||
import { PortfolioCalculator } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator';
|
||||
import { SymbolMetrics, UniqueAsset } from '@ghostfolio/common/interfaces';
|
||||
import {
|
||||
AssetProfileIdentifier,
|
||||
SymbolMetrics
|
||||
} from '@ghostfolio/common/interfaces';
|
||||
import { PortfolioSnapshot, TimelinePosition } from '@ghostfolio/common/models';
|
||||
|
||||
export class MWRPortfolioCalculator extends PortfolioCalculator {
|
||||
@ -27,7 +30,7 @@ export class MWRPortfolioCalculator extends PortfolioCalculator {
|
||||
};
|
||||
start: Date;
|
||||
step?: number;
|
||||
} & UniqueAsset): SymbolMetrics {
|
||||
} & AssetProfileIdentifier): SymbolMetrics {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
}
|
||||
|
@ -19,12 +19,12 @@ import {
|
||||
resetHours
|
||||
} from '@ghostfolio/common/helper';
|
||||
import {
|
||||
AssetProfileIdentifier,
|
||||
DataProviderInfo,
|
||||
HistoricalDataItem,
|
||||
InvestmentItem,
|
||||
ResponseError,
|
||||
SymbolMetrics,
|
||||
UniqueAsset
|
||||
SymbolMetrics
|
||||
} from '@ghostfolio/common/interfaces';
|
||||
import { PortfolioSnapshot, TimelinePosition } from '@ghostfolio/common/models';
|
||||
import { DateRange, GroupBy } from '@ghostfolio/common/types';
|
||||
@ -356,15 +356,15 @@ export abstract class PortfolioCalculator {
|
||||
dataSource: item.dataSource,
|
||||
fee: item.fee,
|
||||
firstBuyDate: item.firstBuyDate,
|
||||
grossPerformance: !hasErrors ? grossPerformance ?? null : null,
|
||||
grossPerformance: !hasErrors ? (grossPerformance ?? null) : null,
|
||||
grossPerformancePercentage: !hasErrors
|
||||
? grossPerformancePercentage ?? null
|
||||
? (grossPerformancePercentage ?? null)
|
||||
: null,
|
||||
grossPerformancePercentageWithCurrencyEffect: !hasErrors
|
||||
? grossPerformancePercentageWithCurrencyEffect ?? null
|
||||
? (grossPerformancePercentageWithCurrencyEffect ?? null)
|
||||
: null,
|
||||
grossPerformanceWithCurrencyEffect: !hasErrors
|
||||
? grossPerformanceWithCurrencyEffect ?? null
|
||||
? (grossPerformanceWithCurrencyEffect ?? null)
|
||||
: null,
|
||||
investment: totalInvestment,
|
||||
investmentWithCurrencyEffect: totalInvestmentWithCurrencyEffect,
|
||||
@ -372,15 +372,15 @@ export abstract class PortfolioCalculator {
|
||||
marketSymbolMap[endDateString]?.[item.symbol]?.toNumber() ?? null,
|
||||
marketPriceInBaseCurrency:
|
||||
marketPriceInBaseCurrency?.toNumber() ?? null,
|
||||
netPerformance: !hasErrors ? netPerformance ?? null : null,
|
||||
netPerformance: !hasErrors ? (netPerformance ?? null) : null,
|
||||
netPerformancePercentage: !hasErrors
|
||||
? netPerformancePercentage ?? null
|
||||
? (netPerformancePercentage ?? null)
|
||||
: null,
|
||||
netPerformancePercentageWithCurrencyEffect: !hasErrors
|
||||
? netPerformancePercentageWithCurrencyEffect ?? null
|
||||
? (netPerformancePercentageWithCurrencyEffect ?? null)
|
||||
: null,
|
||||
netPerformanceWithCurrencyEffect: !hasErrors
|
||||
? netPerformanceWithCurrencyEffect ?? null
|
||||
? (netPerformanceWithCurrencyEffect ?? null)
|
||||
: null,
|
||||
quantity: item.quantity,
|
||||
symbol: item.symbol,
|
||||
@ -905,7 +905,7 @@ export abstract class PortfolioCalculator {
|
||||
};
|
||||
start: Date;
|
||||
step?: number;
|
||||
} & UniqueAsset): SymbolMetrics;
|
||||
} & AssetProfileIdentifier): SymbolMetrics;
|
||||
|
||||
public getTransactionPoints() {
|
||||
return this.transactionPoints;
|
||||
|
@ -2,7 +2,10 @@ import { PortfolioCalculator } from '@ghostfolio/api/app/portfolio/calculator/po
|
||||
import { PortfolioOrderItem } from '@ghostfolio/api/app/portfolio/interfaces/portfolio-order-item.interface';
|
||||
import { getFactor } from '@ghostfolio/api/helper/portfolio.helper';
|
||||
import { DATE_FORMAT } from '@ghostfolio/common/helper';
|
||||
import { SymbolMetrics, UniqueAsset } from '@ghostfolio/common/interfaces';
|
||||
import {
|
||||
AssetProfileIdentifier,
|
||||
SymbolMetrics
|
||||
} from '@ghostfolio/common/interfaces';
|
||||
import { PortfolioSnapshot, TimelinePosition } from '@ghostfolio/common/models';
|
||||
|
||||
import { Logger } from '@nestjs/common';
|
||||
@ -151,7 +154,7 @@ export class TWRPortfolioCalculator extends PortfolioCalculator {
|
||||
};
|
||||
start: Date;
|
||||
step?: number;
|
||||
} & UniqueAsset): SymbolMetrics {
|
||||
} & AssetProfileIdentifier): SymbolMetrics {
|
||||
const currentExchangeRate = exchangeRates[format(new Date(), DATE_FORMAT)];
|
||||
const currentValues: { [date: string]: Big } = {};
|
||||
const currentValuesWithCurrencyEffect: { [date: string]: Big } = {};
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { DataProviderService } from '@ghostfolio/api/services/data-provider/data-provider.service';
|
||||
import { MarketDataService } from '@ghostfolio/api/services/market-data/market-data.service';
|
||||
import { PropertyService } from '@ghostfolio/api/services/property/property.service';
|
||||
import { UniqueAsset } from '@ghostfolio/common/interfaces';
|
||||
import { AssetProfileIdentifier } from '@ghostfolio/common/interfaces';
|
||||
|
||||
import { DataSource, MarketData } from '@prisma/client';
|
||||
|
||||
@ -24,32 +24,32 @@ jest.mock('@ghostfolio/api/services/market-data/market-data.service', () => {
|
||||
});
|
||||
},
|
||||
getRange: ({
|
||||
assetProfileIdentifiers,
|
||||
dateRangeEnd,
|
||||
dateRangeStart,
|
||||
uniqueAssets
|
||||
dateRangeStart
|
||||
}: {
|
||||
assetProfileIdentifiers: AssetProfileIdentifier[];
|
||||
dateRangeEnd: Date;
|
||||
dateRangeStart: Date;
|
||||
uniqueAssets: UniqueAsset[];
|
||||
}) => {
|
||||
return Promise.resolve<MarketData[]>([
|
||||
{
|
||||
createdAt: dateRangeStart,
|
||||
dataSource: uniqueAssets[0].dataSource,
|
||||
dataSource: assetProfileIdentifiers[0].dataSource,
|
||||
date: dateRangeStart,
|
||||
id: '8fa48fde-f397-4b0d-adbc-fb940e830e6d',
|
||||
marketPrice: 1841.823902,
|
||||
state: 'CLOSE',
|
||||
symbol: uniqueAssets[0].symbol
|
||||
symbol: assetProfileIdentifiers[0].symbol
|
||||
},
|
||||
{
|
||||
createdAt: dateRangeEnd,
|
||||
dataSource: uniqueAssets[0].dataSource,
|
||||
dataSource: assetProfileIdentifiers[0].dataSource,
|
||||
date: dateRangeEnd,
|
||||
id: '082d6893-df27-4c91-8a5d-092e84315b56',
|
||||
marketPrice: 1847.839966,
|
||||
state: 'CLOSE',
|
||||
symbol: uniqueAssets[0].symbol
|
||||
symbol: assetProfileIdentifiers[0].symbol
|
||||
}
|
||||
]);
|
||||
}
|
||||
|
@ -3,9 +3,9 @@ import { DataProviderService } from '@ghostfolio/api/services/data-provider/data
|
||||
import { MarketDataService } from '@ghostfolio/api/services/market-data/market-data.service';
|
||||
import { resetHours } from '@ghostfolio/common/helper';
|
||||
import {
|
||||
AssetProfileIdentifier,
|
||||
DataProviderInfo,
|
||||
ResponseError,
|
||||
UniqueAsset
|
||||
ResponseError
|
||||
} from '@ghostfolio/common/interfaces';
|
||||
import type { RequestWithUser } from '@ghostfolio/common/types';
|
||||
|
||||
@ -80,17 +80,16 @@ export class CurrentRateService {
|
||||
);
|
||||
}
|
||||
|
||||
const uniqueAssets: UniqueAsset[] = dataGatheringItems.map(
|
||||
({ dataSource, symbol }) => {
|
||||
const assetProfileIdentifiers: AssetProfileIdentifier[] =
|
||||
dataGatheringItems.map(({ dataSource, symbol }) => {
|
||||
return { dataSource, symbol };
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
promises.push(
|
||||
this.marketDataService
|
||||
.getRange({
|
||||
dateQuery,
|
||||
uniqueAssets
|
||||
assetProfileIdentifiers,
|
||||
dateQuery
|
||||
})
|
||||
.then((data) => {
|
||||
return data.map(({ dataSource, date, marketPrice, symbol }) => {
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { UniqueAsset } from '@ghostfolio/common/interfaces';
|
||||
import { AssetProfileIdentifier } from '@ghostfolio/common/interfaces';
|
||||
|
||||
export interface GetValueObject extends UniqueAsset {
|
||||
export interface GetValueObject extends AssetProfileIdentifier {
|
||||
date: Date;
|
||||
marketPrice: number;
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service';
|
||||
import { getAssetProfileIdentifier } from '@ghostfolio/common/helper';
|
||||
import { UniqueAsset } from '@ghostfolio/common/interfaces';
|
||||
import { AssetProfileIdentifier } from '@ghostfolio/common/interfaces';
|
||||
|
||||
import { CACHE_MANAGER } from '@nestjs/cache-manager';
|
||||
import { Inject, Injectable, Logger } from '@nestjs/common';
|
||||
@ -28,7 +28,7 @@ export class RedisCacheService {
|
||||
return `portfolio-snapshot-${userId}`;
|
||||
}
|
||||
|
||||
public getQuoteKey({ dataSource, symbol }: UniqueAsset) {
|
||||
public getQuoteKey({ dataSource, symbol }: AssetProfileIdentifier) {
|
||||
return `quote-${getAssetProfileIdentifier({ dataSource, symbol })}`;
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,9 @@
|
||||
import { HistoricalDataItem, UniqueAsset } from '@ghostfolio/common/interfaces';
|
||||
import {
|
||||
AssetProfileIdentifier,
|
||||
HistoricalDataItem
|
||||
} from '@ghostfolio/common/interfaces';
|
||||
|
||||
export interface SymbolItem extends UniqueAsset {
|
||||
export interface SymbolItem extends AssetProfileIdentifier {
|
||||
currency: string;
|
||||
historicalData: HistoricalDataItem[];
|
||||
marketPrice: number;
|
||||
|
@ -40,13 +40,13 @@ export class SymbolService {
|
||||
const days = includeHistoricalData;
|
||||
|
||||
const marketData = await this.marketDataService.getRange({
|
||||
dateQuery: { gte: subDays(new Date(), days) },
|
||||
uniqueAssets: [
|
||||
assetProfileIdentifiers: [
|
||||
{
|
||||
dataSource: dataGatheringItem.dataSource,
|
||||
symbol: dataGatheringItem.symbol
|
||||
}
|
||||
]
|
||||
],
|
||||
dateQuery: { gte: subDays(new Date(), days) }
|
||||
});
|
||||
|
||||
historicalData = marketData.map(({ date, marketPrice: value }) => {
|
||||
|
@ -7,7 +7,7 @@ import {
|
||||
GATHER_HISTORICAL_MARKET_DATA_PROCESS
|
||||
} from '@ghostfolio/common/config';
|
||||
import { DATE_FORMAT, getStartOfUtcDate } from '@ghostfolio/common/helper';
|
||||
import { UniqueAsset } from '@ghostfolio/common/interfaces';
|
||||
import { AssetProfileIdentifier } from '@ghostfolio/common/interfaces';
|
||||
|
||||
import { Process, Processor } from '@nestjs/bull';
|
||||
import { Injectable, Logger } from '@nestjs/common';
|
||||
@ -35,7 +35,7 @@ export class DataGatheringProcessor {
|
||||
) {}
|
||||
|
||||
@Process({ concurrency: 1, name: GATHER_ASSET_PROFILE_PROCESS })
|
||||
public async gatherAssetProfile(job: Job<UniqueAsset>) {
|
||||
public async gatherAssetProfile(job: Job<AssetProfileIdentifier>) {
|
||||
try {
|
||||
Logger.log(
|
||||
`Asset profile data gathering has been started for ${job.data.symbol} (${job.data.dataSource})`,
|
||||
|
@ -20,7 +20,10 @@ import {
|
||||
getAssetProfileIdentifier,
|
||||
resetHours
|
||||
} from '@ghostfolio/common/helper';
|
||||
import { BenchmarkProperty, UniqueAsset } from '@ghostfolio/common/interfaces';
|
||||
import {
|
||||
AssetProfileIdentifier,
|
||||
BenchmarkProperty
|
||||
} from '@ghostfolio/common/interfaces';
|
||||
|
||||
import { InjectQueue } from '@nestjs/bull';
|
||||
import { Inject, Injectable, Logger } from '@nestjs/common';
|
||||
@ -91,7 +94,7 @@ export class DataGatheringService {
|
||||
});
|
||||
}
|
||||
|
||||
public async gatherSymbol({ dataSource, symbol }: UniqueAsset) {
|
||||
public async gatherSymbol({ dataSource, symbol }: AssetProfileIdentifier) {
|
||||
await this.marketDataService.deleteMany({ dataSource, symbol });
|
||||
|
||||
const dataGatheringItems = (await this.getSymbolsMax()).filter(
|
||||
@ -146,23 +149,29 @@ export class DataGatheringService {
|
||||
}
|
||||
}
|
||||
|
||||
public async gatherAssetProfiles(aUniqueAssets?: UniqueAsset[]) {
|
||||
let uniqueAssets = aUniqueAssets?.filter((dataGatheringItem) => {
|
||||
return dataGatheringItem.dataSource !== 'MANUAL';
|
||||
});
|
||||
public async gatherAssetProfiles(
|
||||
aAssetProfileIdentifiers?: AssetProfileIdentifier[]
|
||||
) {
|
||||
let assetProfileIdentifiers = aAssetProfileIdentifiers?.filter(
|
||||
(dataGatheringItem) => {
|
||||
return dataGatheringItem.dataSource !== 'MANUAL';
|
||||
}
|
||||
);
|
||||
|
||||
if (!uniqueAssets) {
|
||||
uniqueAssets = await this.getAllAssetProfileIdentifiers();
|
||||
if (!assetProfileIdentifiers) {
|
||||
assetProfileIdentifiers = await this.getAllAssetProfileIdentifiers();
|
||||
}
|
||||
|
||||
if (uniqueAssets.length <= 0) {
|
||||
if (assetProfileIdentifiers.length <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const assetProfiles =
|
||||
await this.dataProviderService.getAssetProfiles(uniqueAssets);
|
||||
const symbolProfiles =
|
||||
await this.symbolProfileService.getSymbolProfiles(uniqueAssets);
|
||||
const assetProfiles = await this.dataProviderService.getAssetProfiles(
|
||||
assetProfileIdentifiers
|
||||
);
|
||||
const symbolProfiles = await this.symbolProfileService.getSymbolProfiles(
|
||||
assetProfileIdentifiers
|
||||
);
|
||||
|
||||
for (const [symbol, assetProfile] of Object.entries(assetProfiles)) {
|
||||
const symbolMapping = symbolProfiles.find((symbolProfile) => {
|
||||
@ -248,7 +257,7 @@ export class DataGatheringService {
|
||||
'DataGatheringService'
|
||||
);
|
||||
|
||||
if (uniqueAssets.length === 1) {
|
||||
if (assetProfileIdentifiers.length === 1) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
@ -284,7 +293,9 @@ export class DataGatheringService {
|
||||
);
|
||||
}
|
||||
|
||||
public async getAllAssetProfileIdentifiers(): Promise<UniqueAsset[]> {
|
||||
public async getAllAssetProfileIdentifiers(): Promise<
|
||||
AssetProfileIdentifier[]
|
||||
> {
|
||||
const symbolProfiles = await this.prismaService.symbolProfile.findMany({
|
||||
orderBy: [{ symbol: 'asc' }]
|
||||
});
|
||||
@ -305,7 +316,7 @@ export class DataGatheringService {
|
||||
}
|
||||
|
||||
private async getAssetProfileIdentifiersWithCompleteMarketData(): Promise<
|
||||
UniqueAsset[]
|
||||
AssetProfileIdentifier[]
|
||||
> {
|
||||
return (
|
||||
await this.prismaService.marketData.groupBy({
|
||||
|
@ -20,7 +20,7 @@ import {
|
||||
getStartOfUtcDate,
|
||||
isDerivedCurrency
|
||||
} from '@ghostfolio/common/helper';
|
||||
import { UniqueAsset } from '@ghostfolio/common/interfaces';
|
||||
import { AssetProfileIdentifier } from '@ghostfolio/common/interfaces';
|
||||
import type { Granularity, UserWithSettings } from '@ghostfolio/common/types';
|
||||
|
||||
import { Inject, Injectable, Logger } from '@nestjs/common';
|
||||
@ -75,7 +75,7 @@ export class DataProviderService {
|
||||
return false;
|
||||
}
|
||||
|
||||
public async getAssetProfiles(items: UniqueAsset[]): Promise<{
|
||||
public async getAssetProfiles(items: AssetProfileIdentifier[]): Promise<{
|
||||
[symbol: string]: Partial<SymbolProfile>;
|
||||
}> {
|
||||
const response: {
|
||||
@ -173,7 +173,7 @@ export class DataProviderService {
|
||||
}
|
||||
|
||||
public async getHistorical(
|
||||
aItems: UniqueAsset[],
|
||||
aItems: AssetProfileIdentifier[],
|
||||
aGranularity: Granularity = 'month',
|
||||
from: Date,
|
||||
to: Date
|
||||
@ -243,7 +243,7 @@ export class DataProviderService {
|
||||
from,
|
||||
to
|
||||
}: {
|
||||
dataGatheringItems: UniqueAsset[];
|
||||
dataGatheringItems: AssetProfileIdentifier[];
|
||||
from: Date;
|
||||
to: Date;
|
||||
}): Promise<{
|
||||
@ -350,7 +350,7 @@ export class DataProviderService {
|
||||
useCache = true,
|
||||
user
|
||||
}: {
|
||||
items: UniqueAsset[];
|
||||
items: AssetProfileIdentifier[];
|
||||
requestTimeout?: number;
|
||||
useCache?: boolean;
|
||||
user?: UserWithSettings;
|
||||
@ -376,7 +376,7 @@ export class DataProviderService {
|
||||
}
|
||||
|
||||
// Get items from cache
|
||||
const itemsToFetch: UniqueAsset[] = [];
|
||||
const itemsToFetch: AssetProfileIdentifier[] = [];
|
||||
|
||||
for (const { dataSource, symbol } of items) {
|
||||
if (useCache) {
|
||||
@ -633,7 +633,7 @@ export class DataProviderService {
|
||||
dataGatheringItems
|
||||
}: {
|
||||
currency: string;
|
||||
dataGatheringItems: UniqueAsset[];
|
||||
dataGatheringItems: AssetProfileIdentifier[];
|
||||
}) {
|
||||
return dataGatheringItems.some(({ dataSource, symbol }) => {
|
||||
return (
|
||||
|
@ -361,13 +361,13 @@ export class ExchangeRateDataService {
|
||||
const symbol = `${currencyFrom}${currencyTo}`;
|
||||
|
||||
const marketData = await this.marketDataService.getRange({
|
||||
dateQuery: { gte: startDate, lt: endDate },
|
||||
uniqueAssets: [
|
||||
assetProfileIdentifiers: [
|
||||
{
|
||||
dataSource,
|
||||
symbol
|
||||
}
|
||||
]
|
||||
],
|
||||
dateQuery: { gte: startDate, lt: endDate }
|
||||
});
|
||||
|
||||
if (marketData?.length > 0) {
|
||||
@ -392,13 +392,13 @@ export class ExchangeRateDataService {
|
||||
}
|
||||
} else {
|
||||
const marketData = await this.marketDataService.getRange({
|
||||
dateQuery: { gte: startDate, lt: endDate },
|
||||
uniqueAssets: [
|
||||
assetProfileIdentifiers: [
|
||||
{
|
||||
dataSource,
|
||||
symbol: `${DEFAULT_CURRENCY}${currencyFrom}`
|
||||
}
|
||||
]
|
||||
],
|
||||
dateQuery: { gte: startDate, lt: endDate }
|
||||
});
|
||||
|
||||
for (const { date, marketPrice } of marketData) {
|
||||
@ -415,16 +415,16 @@ export class ExchangeRateDataService {
|
||||
}
|
||||
} else {
|
||||
const marketData = await this.marketDataService.getRange({
|
||||
dateQuery: {
|
||||
gte: startDate,
|
||||
lt: endDate
|
||||
},
|
||||
uniqueAssets: [
|
||||
assetProfileIdentifiers: [
|
||||
{
|
||||
dataSource,
|
||||
symbol: `${DEFAULT_CURRENCY}${currencyTo}`
|
||||
}
|
||||
]
|
||||
],
|
||||
dateQuery: {
|
||||
gte: startDate,
|
||||
lt: endDate
|
||||
}
|
||||
});
|
||||
|
||||
for (const { date, marketPrice } of marketData) {
|
||||
|
@ -1,4 +1,7 @@
|
||||
import { DataProviderInfo, UniqueAsset } from '@ghostfolio/common/interfaces';
|
||||
import {
|
||||
AssetProfileIdentifier,
|
||||
DataProviderInfo
|
||||
} from '@ghostfolio/common/interfaces';
|
||||
import { MarketState } from '@ghostfolio/common/types';
|
||||
|
||||
import {
|
||||
@ -34,6 +37,6 @@ export interface IDataProviderResponse {
|
||||
marketState: MarketState;
|
||||
}
|
||||
|
||||
export interface IDataGatheringItem extends UniqueAsset {
|
||||
export interface IDataGatheringItem extends AssetProfileIdentifier {
|
||||
date?: Date;
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ import { DateQuery } from '@ghostfolio/api/app/portfolio/interfaces/date-query.i
|
||||
import { IDataGatheringItem } from '@ghostfolio/api/services/interfaces/interfaces';
|
||||
import { PrismaService } from '@ghostfolio/api/services/prisma/prisma.service';
|
||||
import { resetHours } from '@ghostfolio/common/helper';
|
||||
import { UniqueAsset } from '@ghostfolio/common/interfaces';
|
||||
import { AssetProfileIdentifier } from '@ghostfolio/common/interfaces';
|
||||
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import {
|
||||
@ -17,7 +17,7 @@ import {
|
||||
export class MarketDataService {
|
||||
public constructor(private readonly prismaService: PrismaService) {}
|
||||
|
||||
public async deleteMany({ dataSource, symbol }: UniqueAsset) {
|
||||
public async deleteMany({ dataSource, symbol }: AssetProfileIdentifier) {
|
||||
return this.prismaService.marketData.deleteMany({
|
||||
where: {
|
||||
dataSource,
|
||||
@ -40,7 +40,7 @@ export class MarketDataService {
|
||||
});
|
||||
}
|
||||
|
||||
public async getMax({ dataSource, symbol }: UniqueAsset) {
|
||||
public async getMax({ dataSource, symbol }: AssetProfileIdentifier) {
|
||||
return this.prismaService.marketData.findFirst({
|
||||
select: {
|
||||
date: true,
|
||||
@ -59,11 +59,11 @@ export class MarketDataService {
|
||||
}
|
||||
|
||||
public async getRange({
|
||||
dateQuery,
|
||||
uniqueAssets
|
||||
assetProfileIdentifiers,
|
||||
dateQuery
|
||||
}: {
|
||||
assetProfileIdentifiers: AssetProfileIdentifier[];
|
||||
dateQuery: DateQuery;
|
||||
uniqueAssets: UniqueAsset[];
|
||||
}): Promise<MarketData[]> {
|
||||
return this.prismaService.marketData.findMany({
|
||||
orderBy: [
|
||||
@ -76,13 +76,13 @@ export class MarketDataService {
|
||||
],
|
||||
where: {
|
||||
dataSource: {
|
||||
in: uniqueAssets.map(({ dataSource }) => {
|
||||
in: assetProfileIdentifiers.map(({ dataSource }) => {
|
||||
return dataSource;
|
||||
})
|
||||
},
|
||||
date: dateQuery,
|
||||
symbol: {
|
||||
in: uniqueAssets.map(({ symbol }) => {
|
||||
in: assetProfileIdentifiers.map(({ symbol }) => {
|
||||
return symbol;
|
||||
})
|
||||
}
|
||||
|
@ -1,10 +1,10 @@
|
||||
import { PrismaService } from '@ghostfolio/api/services/prisma/prisma.service';
|
||||
import { UNKNOWN_KEY } from '@ghostfolio/common/config';
|
||||
import {
|
||||
AssetProfileIdentifier,
|
||||
EnhancedSymbolProfile,
|
||||
Holding,
|
||||
ScraperConfiguration,
|
||||
UniqueAsset
|
||||
ScraperConfiguration
|
||||
} from '@ghostfolio/common/interfaces';
|
||||
import { Country } from '@ghostfolio/common/interfaces/country.interface';
|
||||
import { Sector } from '@ghostfolio/common/interfaces/sector.interface';
|
||||
@ -23,7 +23,7 @@ export class SymbolProfileService {
|
||||
return this.prismaService.symbolProfile.create({ data: assetProfile });
|
||||
}
|
||||
|
||||
public async delete({ dataSource, symbol }: UniqueAsset) {
|
||||
public async delete({ dataSource, symbol }: AssetProfileIdentifier) {
|
||||
return this.prismaService.symbolProfile.delete({
|
||||
where: { dataSource_symbol: { dataSource, symbol } }
|
||||
});
|
||||
@ -36,7 +36,7 @@ export class SymbolProfileService {
|
||||
}
|
||||
|
||||
public async getSymbolProfiles(
|
||||
aUniqueAssets: UniqueAsset[]
|
||||
aAssetProfileIdentifiers: AssetProfileIdentifier[]
|
||||
): Promise<EnhancedSymbolProfile[]> {
|
||||
return this.prismaService.symbolProfile
|
||||
.findMany({
|
||||
@ -54,7 +54,7 @@ export class SymbolProfileService {
|
||||
SymbolProfileOverrides: true
|
||||
},
|
||||
where: {
|
||||
OR: aUniqueAssets.map(({ dataSource, symbol }) => {
|
||||
OR: aAssetProfileIdentifiers.map(({ dataSource, symbol }) => {
|
||||
return {
|
||||
dataSource,
|
||||
symbol
|
||||
@ -140,7 +140,7 @@ export class SymbolProfileService {
|
||||
symbolMapping,
|
||||
SymbolProfileOverrides,
|
||||
url
|
||||
}: Prisma.SymbolProfileUpdateInput & UniqueAsset) {
|
||||
}: AssetProfileIdentifier & Prisma.SymbolProfileUpdateInput) {
|
||||
return this.prismaService.symbolProfile.update({
|
||||
data: {
|
||||
assetClass,
|
||||
|
@ -7,9 +7,9 @@ import {
|
||||
} from '@ghostfolio/common/config';
|
||||
import { getDateFormatString } from '@ghostfolio/common/helper';
|
||||
import {
|
||||
AssetProfileIdentifier,
|
||||
Filter,
|
||||
InfoItem,
|
||||
UniqueAsset,
|
||||
User
|
||||
} from '@ghostfolio/common/interfaces';
|
||||
import { AdminMarketDataItem } from '@ghostfolio/common/interfaces/admin-market-data.interface';
|
||||
@ -225,7 +225,7 @@ export class AdminMarketDataComponent
|
||||
});
|
||||
}
|
||||
|
||||
public onDeleteAssetProfile({ dataSource, symbol }: UniqueAsset) {
|
||||
public onDeleteAssetProfile({ dataSource, symbol }: AssetProfileIdentifier) {
|
||||
this.adminMarketDataService.deleteAssetProfile({ dataSource, symbol });
|
||||
}
|
||||
|
||||
@ -266,21 +266,27 @@ export class AdminMarketDataComponent
|
||||
.subscribe(() => {});
|
||||
}
|
||||
|
||||
public onGatherProfileDataBySymbol({ dataSource, symbol }: UniqueAsset) {
|
||||
public onGatherProfileDataBySymbol({
|
||||
dataSource,
|
||||
symbol
|
||||
}: AssetProfileIdentifier) {
|
||||
this.adminService
|
||||
.gatherProfileDataBySymbol({ dataSource, symbol })
|
||||
.pipe(takeUntil(this.unsubscribeSubject))
|
||||
.subscribe(() => {});
|
||||
}
|
||||
|
||||
public onGatherSymbol({ dataSource, symbol }: UniqueAsset) {
|
||||
public onGatherSymbol({ dataSource, symbol }: AssetProfileIdentifier) {
|
||||
this.adminService
|
||||
.gatherSymbol({ dataSource, symbol })
|
||||
.pipe(takeUntil(this.unsubscribeSubject))
|
||||
.subscribe(() => {});
|
||||
}
|
||||
|
||||
public onOpenAssetProfileDialog({ dataSource, symbol }: UniqueAsset) {
|
||||
public onOpenAssetProfileDialog({
|
||||
dataSource,
|
||||
symbol
|
||||
}: AssetProfileIdentifier) {
|
||||
this.router.navigate([], {
|
||||
queryParams: {
|
||||
dataSource,
|
||||
|
@ -2,8 +2,8 @@ import { AdminService } from '@ghostfolio/client/services/admin.service';
|
||||
import { ghostfolioScraperApiSymbolPrefix } from '@ghostfolio/common/config';
|
||||
import { getCurrencyFromSymbol, isCurrency } from '@ghostfolio/common/helper';
|
||||
import {
|
||||
AdminMarketDataItem,
|
||||
UniqueAsset
|
||||
AssetProfileIdentifier,
|
||||
AdminMarketDataItem
|
||||
} from '@ghostfolio/common/interfaces';
|
||||
|
||||
import { Injectable } from '@angular/core';
|
||||
@ -13,7 +13,7 @@ import { EMPTY, catchError, finalize, forkJoin, takeUntil } from 'rxjs';
|
||||
export class AdminMarketDataService {
|
||||
public constructor(private adminService: AdminService) {}
|
||||
|
||||
public deleteAssetProfile({ dataSource, symbol }: UniqueAsset) {
|
||||
public deleteAssetProfile({ dataSource, symbol }: AssetProfileIdentifier) {
|
||||
const confirmation = confirm(
|
||||
$localize`Do you really want to delete this asset profile?`
|
||||
);
|
||||
@ -29,15 +29,19 @@ export class AdminMarketDataService {
|
||||
}
|
||||
}
|
||||
|
||||
public deleteAssetProfiles(uniqueAssets: UniqueAsset[]) {
|
||||
public deleteAssetProfiles(
|
||||
aAssetProfileIdentifiers: AssetProfileIdentifier[]
|
||||
) {
|
||||
const confirmation = confirm(
|
||||
$localize`Do you really want to delete these profiles?`
|
||||
);
|
||||
|
||||
if (confirmation) {
|
||||
const deleteRequests = uniqueAssets.map(({ dataSource, symbol }) => {
|
||||
return this.adminService.deleteProfileData({ dataSource, symbol });
|
||||
});
|
||||
const deleteRequests = aAssetProfileIdentifiers.map(
|
||||
({ dataSource, symbol }) => {
|
||||
return this.adminService.deleteProfileData({ dataSource, symbol });
|
||||
}
|
||||
);
|
||||
|
||||
forkJoin(deleteRequests)
|
||||
.pipe(
|
||||
|
@ -8,7 +8,7 @@ import { ghostfolioScraperApiSymbolPrefix } from '@ghostfolio/common/config';
|
||||
import { DATE_FORMAT } from '@ghostfolio/common/helper';
|
||||
import {
|
||||
AdminMarketDataDetails,
|
||||
UniqueAsset
|
||||
AssetProfileIdentifier
|
||||
} from '@ghostfolio/common/interfaces';
|
||||
import { translate } from '@ghostfolio/ui/i18n';
|
||||
|
||||
@ -175,20 +175,23 @@ export class AssetProfileDialog implements OnDestroy, OnInit {
|
||||
this.dialogRef.close();
|
||||
}
|
||||
|
||||
public onDeleteProfileData({ dataSource, symbol }: UniqueAsset) {
|
||||
public onDeleteProfileData({ dataSource, symbol }: AssetProfileIdentifier) {
|
||||
this.adminMarketDataService.deleteAssetProfile({ dataSource, symbol });
|
||||
|
||||
this.dialogRef.close();
|
||||
}
|
||||
|
||||
public onGatherProfileDataBySymbol({ dataSource, symbol }: UniqueAsset) {
|
||||
public onGatherProfileDataBySymbol({
|
||||
dataSource,
|
||||
symbol
|
||||
}: AssetProfileIdentifier) {
|
||||
this.adminService
|
||||
.gatherProfileDataBySymbol({ dataSource, symbol })
|
||||
.pipe(takeUntil(this.unsubscribeSubject))
|
||||
.subscribe(() => {});
|
||||
}
|
||||
|
||||
public onGatherSymbol({ dataSource, symbol }: UniqueAsset) {
|
||||
public onGatherSymbol({ dataSource, symbol }: AssetProfileIdentifier) {
|
||||
this.adminService
|
||||
.gatherSymbol({ dataSource, symbol })
|
||||
.pipe(takeUntil(this.unsubscribeSubject))
|
||||
@ -242,7 +245,7 @@ export class AssetProfileDialog implements OnDestroy, OnInit {
|
||||
}
|
||||
}
|
||||
|
||||
public onSetBenchmark({ dataSource, symbol }: UniqueAsset) {
|
||||
public onSetBenchmark({ dataSource, symbol }: AssetProfileIdentifier) {
|
||||
this.dataService
|
||||
.postBenchmark({ dataSource, symbol })
|
||||
.pipe(takeUntil(this.unsubscribeSubject))
|
||||
@ -342,7 +345,7 @@ export class AssetProfileDialog implements OnDestroy, OnInit {
|
||||
});
|
||||
}
|
||||
|
||||
public onUnsetBenchmark({ dataSource, symbol }: UniqueAsset) {
|
||||
public onUnsetBenchmark({ dataSource, symbol }: AssetProfileIdentifier) {
|
||||
this.dataService
|
||||
.deleteBenchmark({ dataSource, symbol })
|
||||
.pipe(takeUntil(this.unsubscribeSubject))
|
||||
|
@ -2,8 +2,8 @@ import { DataService } from '@ghostfolio/client/services/data.service';
|
||||
import { ImpersonationStorageService } from '@ghostfolio/client/services/impersonation-storage.service';
|
||||
import { UserService } from '@ghostfolio/client/services/user/user.service';
|
||||
import {
|
||||
AssetProfileIdentifier,
|
||||
PortfolioPosition,
|
||||
UniqueAsset,
|
||||
User
|
||||
} from '@ghostfolio/common/interfaces';
|
||||
import { hasPermission, permissions } from '@ghostfolio/common/permissions';
|
||||
@ -108,7 +108,7 @@ export class HomeHoldingsComponent implements OnDestroy, OnInit {
|
||||
this.initialize();
|
||||
}
|
||||
|
||||
public onSymbolClicked({ dataSource, symbol }: UniqueAsset) {
|
||||
public onSymbolClicked({ dataSource, symbol }: AssetProfileIdentifier) {
|
||||
if (dataSource && symbol) {
|
||||
this.router.navigate([], {
|
||||
queryParams: { dataSource, symbol, holdingDetailDialog: true }
|
||||
|
@ -5,9 +5,9 @@ import { ImpersonationStorageService } from '@ghostfolio/client/services/imperso
|
||||
import { UserService } from '@ghostfolio/client/services/user/user.service';
|
||||
import { NUMERICAL_PRECISION_THRESHOLD } from '@ghostfolio/common/config';
|
||||
import {
|
||||
AssetProfileIdentifier,
|
||||
LineChartItem,
|
||||
PortfolioPerformance,
|
||||
UniqueAsset,
|
||||
User
|
||||
} from '@ghostfolio/common/interfaces';
|
||||
import { hasPermission, permissions } from '@ghostfolio/common/permissions';
|
||||
@ -26,7 +26,7 @@ import { takeUntil } from 'rxjs/operators';
|
||||
export class HomeOverviewComponent implements OnDestroy, OnInit {
|
||||
public dateRangeOptions = ToggleComponent.DEFAULT_DATE_RANGE_OPTIONS;
|
||||
public deviceType: string;
|
||||
public errors: UniqueAsset[];
|
||||
public errors: AssetProfileIdentifier[];
|
||||
public hasError: boolean;
|
||||
public hasImpersonationId: boolean;
|
||||
public hasPermissionToCreateOrder: boolean;
|
||||
|
@ -38,6 +38,7 @@ import { ImportActivitiesDialogParams } from './interfaces/interfaces';
|
||||
export class ImportActivitiesDialog implements OnDestroy {
|
||||
public accounts: CreateAccountDto[] = [];
|
||||
public activities: Activity[] = [];
|
||||
public assetProfileForm: FormGroup;
|
||||
public dataSource: MatTableDataSource<Activity>;
|
||||
public details: any[] = [];
|
||||
public deviceType: string;
|
||||
@ -53,7 +54,6 @@ export class ImportActivitiesDialog implements OnDestroy {
|
||||
public sortDirection: SortDirection = 'desc';
|
||||
public stepperOrientation: StepperOrientation;
|
||||
public totalItems: number;
|
||||
public uniqueAssetForm: FormGroup;
|
||||
|
||||
private unsubscribeSubject = new Subject<void>();
|
||||
|
||||
@ -73,8 +73,8 @@ export class ImportActivitiesDialog implements OnDestroy {
|
||||
this.stepperOrientation =
|
||||
this.deviceType === 'mobile' ? 'vertical' : 'horizontal';
|
||||
|
||||
this.uniqueAssetForm = this.formBuilder.group({
|
||||
uniqueAsset: [undefined, Validators.required]
|
||||
this.assetProfileForm = this.formBuilder.group({
|
||||
assetProfileIdentifier: [undefined, Validators.required]
|
||||
});
|
||||
|
||||
if (
|
||||
@ -85,7 +85,7 @@ export class ImportActivitiesDialog implements OnDestroy {
|
||||
|
||||
this.dialogTitle = $localize`Import Dividends`;
|
||||
this.mode = 'DIVIDEND';
|
||||
this.uniqueAssetForm.get('uniqueAsset').disable();
|
||||
this.assetProfileForm.get('assetProfileIdentifier').disable();
|
||||
|
||||
this.dataService
|
||||
.fetchPortfolioHoldings({
|
||||
@ -102,7 +102,7 @@ export class ImportActivitiesDialog implements OnDestroy {
|
||||
this.holdings = sortBy(holdings, ({ name }) => {
|
||||
return name.toLowerCase();
|
||||
});
|
||||
this.uniqueAssetForm.get('uniqueAsset').enable();
|
||||
this.assetProfileForm.get('assetProfileIdentifier').enable();
|
||||
|
||||
this.isLoading = false;
|
||||
|
||||
@ -167,10 +167,11 @@ export class ImportActivitiesDialog implements OnDestroy {
|
||||
}
|
||||
|
||||
public onLoadDividends(aStepper: MatStepper) {
|
||||
this.uniqueAssetForm.get('uniqueAsset').disable();
|
||||
this.assetProfileForm.get('assetProfileIdentifier').disable();
|
||||
|
||||
const { dataSource, symbol } =
|
||||
this.uniqueAssetForm.get('uniqueAsset').value;
|
||||
const { dataSource, symbol } = this.assetProfileForm.get(
|
||||
'assetProfileIdentifier'
|
||||
).value;
|
||||
|
||||
this.dataService
|
||||
.fetchDividendsImport({
|
||||
@ -193,7 +194,7 @@ export class ImportActivitiesDialog implements OnDestroy {
|
||||
this.details = [];
|
||||
this.errorMessages = [];
|
||||
this.importStep = ImportStep.SELECT_ACTIVITIES;
|
||||
this.uniqueAssetForm.get('uniqueAsset').enable();
|
||||
this.assetProfileForm.get('assetProfileIdentifier').enable();
|
||||
|
||||
aStepper.reset();
|
||||
}
|
||||
|
@ -25,14 +25,14 @@
|
||||
<div class="pt-3">
|
||||
@if (mode === 'DIVIDEND') {
|
||||
<form
|
||||
[formGroup]="uniqueAssetForm"
|
||||
[formGroup]="assetProfileForm"
|
||||
(ngSubmit)="onLoadDividends(stepper)"
|
||||
>
|
||||
<mat-form-field appearance="outline" class="w-100">
|
||||
<mat-label i18n>Holding</mat-label>
|
||||
<mat-select formControlName="uniqueAsset">
|
||||
<mat-select formControlName="assetProfileIdentifier">
|
||||
<mat-select-trigger>{{
|
||||
uniqueAssetForm.get('uniqueAsset')?.value?.name
|
||||
assetProfileForm.get('assetProfileIdentifier')?.value?.name
|
||||
}}</mat-select-trigger>
|
||||
@for (holding of holdings; track holding) {
|
||||
<mat-option
|
||||
@ -63,7 +63,7 @@
|
||||
color="primary"
|
||||
mat-flat-button
|
||||
type="submit"
|
||||
[disabled]="!uniqueAssetForm.valid"
|
||||
[disabled]="!assetProfileForm.valid"
|
||||
>
|
||||
<span i18n>Load Dividends</span>
|
||||
</button>
|
||||
|
@ -6,10 +6,10 @@ import { UserService } from '@ghostfolio/client/services/user/user.service';
|
||||
import { MAX_TOP_HOLDINGS, UNKNOWN_KEY } from '@ghostfolio/common/config';
|
||||
import { prettifySymbol } from '@ghostfolio/common/helper';
|
||||
import {
|
||||
AssetProfileIdentifier,
|
||||
Holding,
|
||||
PortfolioDetails,
|
||||
PortfolioPosition,
|
||||
UniqueAsset,
|
||||
User
|
||||
} from '@ghostfolio/common/interfaces';
|
||||
import { Market, MarketAdvanced } from '@ghostfolio/common/types';
|
||||
@ -161,7 +161,7 @@ export class AllocationsPageComponent implements OnDestroy, OnInit {
|
||||
this.initialize();
|
||||
}
|
||||
|
||||
public onAccountChartClicked({ symbol }: UniqueAsset) {
|
||||
public onAccountChartClicked({ symbol }: AssetProfileIdentifier) {
|
||||
if (symbol && symbol !== UNKNOWN_KEY) {
|
||||
this.router.navigate([], {
|
||||
queryParams: { accountId: symbol, accountDetailDialog: true }
|
||||
@ -169,7 +169,7 @@ export class AllocationsPageComponent implements OnDestroy, OnInit {
|
||||
}
|
||||
}
|
||||
|
||||
public onSymbolChartClicked({ dataSource, symbol }: UniqueAsset) {
|
||||
public onSymbolChartClicked({ dataSource, symbol }: AssetProfileIdentifier) {
|
||||
if (dataSource && symbol) {
|
||||
this.router.navigate([], {
|
||||
queryParams: { dataSource, symbol, holdingDetailDialog: true }
|
||||
|
@ -7,13 +7,13 @@ import { UpdateTagDto } from '@ghostfolio/api/app/tag/update-tag.dto';
|
||||
import { IDataProviderHistoricalResponse } from '@ghostfolio/api/services/interfaces/interfaces';
|
||||
import { DATE_FORMAT } from '@ghostfolio/common/helper';
|
||||
import {
|
||||
AssetProfileIdentifier,
|
||||
AdminData,
|
||||
AdminJobs,
|
||||
AdminMarketData,
|
||||
AdminMarketDataDetails,
|
||||
EnhancedSymbolProfile,
|
||||
Filter,
|
||||
UniqueAsset
|
||||
Filter
|
||||
} from '@ghostfolio/common/interfaces';
|
||||
|
||||
import { HttpClient, HttpParams } from '@angular/common/http';
|
||||
@ -35,7 +35,7 @@ export class AdminService {
|
||||
private http: HttpClient
|
||||
) {}
|
||||
|
||||
public addAssetProfile({ dataSource, symbol }: UniqueAsset) {
|
||||
public addAssetProfile({ dataSource, symbol }: AssetProfileIdentifier) {
|
||||
return this.http.post<void>(
|
||||
`/api/v1/admin/profile-data/${dataSource}/${symbol}`,
|
||||
null
|
||||
@ -62,7 +62,7 @@ export class AdminService {
|
||||
return this.http.delete<void>(`/api/v1/platform/${aId}`);
|
||||
}
|
||||
|
||||
public deleteProfileData({ dataSource, symbol }: UniqueAsset) {
|
||||
public deleteProfileData({ dataSource, symbol }: AssetProfileIdentifier) {
|
||||
return this.http.delete<void>(
|
||||
`/api/v1/admin/profile-data/${dataSource}/${symbol}`
|
||||
);
|
||||
@ -167,7 +167,10 @@ export class AdminService {
|
||||
return this.http.post<void>('/api/v1/admin/gather/profile-data', {});
|
||||
}
|
||||
|
||||
public gatherProfileDataBySymbol({ dataSource, symbol }: UniqueAsset) {
|
||||
public gatherProfileDataBySymbol({
|
||||
dataSource,
|
||||
symbol
|
||||
}: AssetProfileIdentifier) {
|
||||
return this.http.post<void>(
|
||||
`/api/v1/admin/gather/profile-data/${dataSource}/${symbol}`,
|
||||
{}
|
||||
@ -178,7 +181,7 @@ export class AdminService {
|
||||
dataSource,
|
||||
date,
|
||||
symbol
|
||||
}: UniqueAsset & {
|
||||
}: AssetProfileIdentifier & {
|
||||
date?: Date;
|
||||
}) {
|
||||
let url = `/api/v1/admin/gather/${dataSource}/${symbol}`;
|
||||
@ -217,7 +220,7 @@ export class AdminService {
|
||||
symbol,
|
||||
symbolMapping,
|
||||
url
|
||||
}: UniqueAsset & UpdateAssetProfileDto) {
|
||||
}: AssetProfileIdentifier & UpdateAssetProfileDto) {
|
||||
return this.http.patch<EnhancedSymbolProfile>(
|
||||
`/api/v1/admin/profile-data/${dataSource}/${symbol}`,
|
||||
{
|
||||
@ -272,7 +275,7 @@ export class AdminService {
|
||||
dataSource,
|
||||
scraperConfiguration,
|
||||
symbol
|
||||
}: UniqueAsset & UpdateAssetProfileDto['scraperConfiguration']) {
|
||||
}: AssetProfileIdentifier & UpdateAssetProfileDto['scraperConfiguration']) {
|
||||
return this.http.post<any>(
|
||||
`/api/v1/admin/market-data/${dataSource}/${symbol}/test`,
|
||||
{
|
||||
|
@ -20,6 +20,7 @@ import {
|
||||
AccountBalancesResponse,
|
||||
Accounts,
|
||||
AdminMarketDataDetails,
|
||||
AssetProfileIdentifier,
|
||||
BenchmarkMarketDataDetails,
|
||||
BenchmarkResponse,
|
||||
Export,
|
||||
@ -34,7 +35,6 @@ import {
|
||||
PortfolioPerformanceResponse,
|
||||
PortfolioPublicDetails,
|
||||
PortfolioReport,
|
||||
UniqueAsset,
|
||||
User
|
||||
} from '@ghostfolio/common/interfaces';
|
||||
import { filterGlobalPermissions } from '@ghostfolio/common/permissions';
|
||||
@ -230,7 +230,7 @@ export class DataService {
|
||||
});
|
||||
}
|
||||
|
||||
public fetchDividendsImport({ dataSource, symbol }: UniqueAsset) {
|
||||
public fetchDividendsImport({ dataSource, symbol }: AssetProfileIdentifier) {
|
||||
return this.http.get<ImportResponse>(
|
||||
`/api/v1/import/dividends/${dataSource}/${symbol}`
|
||||
);
|
||||
@ -270,7 +270,7 @@ export class DataService {
|
||||
return this.http.delete<any>(`/api/v1/order/${aId}`);
|
||||
}
|
||||
|
||||
public deleteBenchmark({ dataSource, symbol }: UniqueAsset) {
|
||||
public deleteBenchmark({ dataSource, symbol }: AssetProfileIdentifier) {
|
||||
return this.http.delete<any>(`/api/v1/benchmark/${dataSource}/${symbol}`);
|
||||
}
|
||||
|
||||
@ -289,7 +289,7 @@ export class DataService {
|
||||
public fetchAsset({
|
||||
dataSource,
|
||||
symbol
|
||||
}: UniqueAsset): Observable<AdminMarketDataDetails> {
|
||||
}: AssetProfileIdentifier): Observable<AdminMarketDataDetails> {
|
||||
return this.http.get<any>(`/api/v1/asset/${dataSource}/${symbol}`).pipe(
|
||||
map((data) => {
|
||||
for (const item of data.marketData) {
|
||||
@ -308,7 +308,7 @@ export class DataService {
|
||||
}: {
|
||||
range: DateRange;
|
||||
startDate: Date;
|
||||
} & UniqueAsset): Observable<BenchmarkMarketDataDetails> {
|
||||
} & AssetProfileIdentifier): Observable<BenchmarkMarketDataDetails> {
|
||||
let params = new HttpParams();
|
||||
|
||||
if (range) {
|
||||
@ -630,7 +630,7 @@ export class DataService {
|
||||
);
|
||||
}
|
||||
|
||||
public postBenchmark(benchmark: UniqueAsset) {
|
||||
public postBenchmark(benchmark: AssetProfileIdentifier) {
|
||||
return this.http.post(`/api/v1/benchmark`, benchmark);
|
||||
}
|
||||
|
||||
@ -654,7 +654,7 @@ export class DataService {
|
||||
dataSource,
|
||||
symbol,
|
||||
tags
|
||||
}: { tags: Tag[] } & UniqueAsset) {
|
||||
}: { tags: Tag[] } & AssetProfileIdentifier) {
|
||||
return this.http.put<void>(
|
||||
`/api/v1/portfolio/position/${dataSource}/${symbol}/tags`,
|
||||
{ tags }
|
||||
|
@ -19,7 +19,7 @@ import {
|
||||
ghostfolioScraperApiSymbolPrefix,
|
||||
locale
|
||||
} from './config';
|
||||
import { Benchmark, UniqueAsset } from './interfaces';
|
||||
import { AssetProfileIdentifier, Benchmark } from './interfaces';
|
||||
import { BenchmarkTrend, ColorScheme } from './types';
|
||||
|
||||
export const DATE_FORMAT = 'yyyy-MM-dd';
|
||||
@ -147,7 +147,10 @@ export function getAllActivityTypes(): ActivityType[] {
|
||||
return Object.values(ActivityType);
|
||||
}
|
||||
|
||||
export function getAssetProfileIdentifier({ dataSource, symbol }: UniqueAsset) {
|
||||
export function getAssetProfileIdentifier({
|
||||
dataSource,
|
||||
symbol
|
||||
}: AssetProfileIdentifier) {
|
||||
return `${dataSource}-${symbol}`;
|
||||
}
|
||||
|
||||
@ -377,7 +380,7 @@ export function parseDate(date: string): Date | null {
|
||||
return parseISO(date);
|
||||
}
|
||||
|
||||
export function parseSymbol({ dataSource, symbol }: UniqueAsset) {
|
||||
export function parseSymbol({ dataSource, symbol }: AssetProfileIdentifier) {
|
||||
const [ticker, exchange] = symbol.split('.');
|
||||
|
||||
return {
|
||||
|
@ -1,13 +1,13 @@
|
||||
import { Role } from '@prisma/client';
|
||||
import { AssetProfileIdentifier } from '@ghostfolio/common/interfaces';
|
||||
|
||||
import { UniqueAsset } from './unique-asset.interface';
|
||||
import { Role } from '@prisma/client';
|
||||
|
||||
export interface AdminData {
|
||||
exchangeRates: ({
|
||||
label1: string;
|
||||
label2: string;
|
||||
value: number;
|
||||
} & UniqueAsset)[];
|
||||
} & AssetProfileIdentifier)[];
|
||||
settings: { [key: string]: boolean | object | string | string[] };
|
||||
transactionCount: number;
|
||||
userCount: number;
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { DataSource } from '@prisma/client';
|
||||
|
||||
export interface UniqueAsset {
|
||||
export interface AssetProfileIdentifier {
|
||||
dataSource: DataSource;
|
||||
symbol: string;
|
||||
}
|
@ -7,6 +7,7 @@ import type {
|
||||
AdminMarketData,
|
||||
AdminMarketDataItem
|
||||
} from './admin-market-data.interface';
|
||||
import type { AssetProfileIdentifier } from './asset-profile-identifier.interface';
|
||||
import type { BenchmarkMarketDataDetails } from './benchmark-market-data-details.interface';
|
||||
import type { BenchmarkProperty } from './benchmark-property.interface';
|
||||
import type { Benchmark } from './benchmark.interface';
|
||||
@ -48,7 +49,6 @@ import type { Subscription } from './subscription.interface';
|
||||
import type { SymbolMetrics } from './symbol-metrics.interface';
|
||||
import type { SystemMessage } from './system-message.interface';
|
||||
import type { TabConfiguration } from './tab-configuration.interface';
|
||||
import type { UniqueAsset } from './unique-asset.interface';
|
||||
import type { UserSettings } from './user-settings.interface';
|
||||
import type { User } from './user.interface';
|
||||
|
||||
@ -61,6 +61,7 @@ export {
|
||||
AdminMarketData,
|
||||
AdminMarketDataDetails,
|
||||
AdminMarketDataItem,
|
||||
AssetProfileIdentifier,
|
||||
Benchmark,
|
||||
BenchmarkMarketDataDetails,
|
||||
BenchmarkProperty,
|
||||
@ -101,7 +102,6 @@ export {
|
||||
Subscription,
|
||||
SymbolMetrics,
|
||||
TabConfiguration,
|
||||
UniqueAsset,
|
||||
User,
|
||||
UserSettings
|
||||
};
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { UniqueAsset } from '../unique-asset.interface';
|
||||
import { AssetProfileIdentifier } from '@ghostfolio/common/interfaces';
|
||||
|
||||
export interface ResponseError {
|
||||
errors?: UniqueAsset[];
|
||||
errors?: AssetProfileIdentifier[];
|
||||
hasErrors: boolean;
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { transformToBig } from '@ghostfolio/common/class-transformer';
|
||||
import { UniqueAsset } from '@ghostfolio/common/interfaces';
|
||||
import { AssetProfileIdentifier } from '@ghostfolio/common/interfaces';
|
||||
import { TimelinePosition } from '@ghostfolio/common/models';
|
||||
|
||||
import { Big } from 'big.js';
|
||||
@ -9,7 +9,7 @@ export class PortfolioSnapshot {
|
||||
@Transform(transformToBig, { toClassOnly: true })
|
||||
@Type(() => Big)
|
||||
currentValueInBaseCurrency: Big;
|
||||
errors?: UniqueAsset[];
|
||||
errors?: AssetProfileIdentifier[];
|
||||
|
||||
@Transform(transformToBig, { toClassOnly: true })
|
||||
@Type(() => Big)
|
||||
|
@ -3,7 +3,7 @@ import { GfAssetProfileIconComponent } from '@ghostfolio/client/components/asset
|
||||
import { GfSymbolModule } from '@ghostfolio/client/pipes/symbol/symbol.module';
|
||||
import { DEFAULT_PAGE_SIZE } from '@ghostfolio/common/config';
|
||||
import { getDateFormatString, getLocale } from '@ghostfolio/common/helper';
|
||||
import { UniqueAsset } from '@ghostfolio/common/interfaces';
|
||||
import { AssetProfileIdentifier } from '@ghostfolio/common/interfaces';
|
||||
import { OrderWithAccount } from '@ghostfolio/common/types';
|
||||
import { GfActivityTypeComponent } from '@ghostfolio/ui/activity-type';
|
||||
import { GfNoTransactionsInfoComponent } from '@ghostfolio/ui/no-transactions-info';
|
||||
@ -99,7 +99,7 @@ export class GfActivitiesTableComponent
|
||||
@Output() export = new EventEmitter<void>();
|
||||
@Output() exportDrafts = new EventEmitter<string[]>();
|
||||
@Output() import = new EventEmitter<void>();
|
||||
@Output() importDividends = new EventEmitter<UniqueAsset>();
|
||||
@Output() importDividends = new EventEmitter<AssetProfileIdentifier>();
|
||||
@Output() pageChanged = new EventEmitter<PageEvent>();
|
||||
@Output() selectedActivities = new EventEmitter<Activity[]>();
|
||||
@Output() sortChanged = new EventEmitter<Sort>();
|
||||
@ -263,7 +263,7 @@ export class GfActivitiesTableComponent
|
||||
alert(aComment);
|
||||
}
|
||||
|
||||
public onOpenPositionDialog({ dataSource, symbol }: UniqueAsset) {
|
||||
public onOpenPositionDialog({ dataSource, symbol }: AssetProfileIdentifier) {
|
||||
this.router.navigate([], {
|
||||
queryParams: { dataSource, symbol, holdingDetailDialog: true }
|
||||
});
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { UniqueAsset } from '@ghostfolio/common/interfaces';
|
||||
import { AssetProfileIdentifier } from '@ghostfolio/common/interfaces';
|
||||
import { DateRange } from '@ghostfolio/common/types';
|
||||
|
||||
export interface IDateRangeOption {
|
||||
@ -6,7 +6,7 @@ export interface IDateRangeOption {
|
||||
value: DateRange;
|
||||
}
|
||||
|
||||
export interface ISearchResultItem extends UniqueAsset {
|
||||
export interface ISearchResultItem extends AssetProfileIdentifier {
|
||||
assetSubClassString: string;
|
||||
currency: string;
|
||||
name: string;
|
||||
|
@ -1,5 +1,9 @@
|
||||
import { getLocale, resolveMarketCondition } from '@ghostfolio/common/helper';
|
||||
import { Benchmark, UniqueAsset, User } from '@ghostfolio/common/interfaces';
|
||||
import {
|
||||
AssetProfileIdentifier,
|
||||
Benchmark,
|
||||
User
|
||||
} from '@ghostfolio/common/interfaces';
|
||||
import { translate } from '@ghostfolio/ui/i18n';
|
||||
import { GfTrendIndicatorComponent } from '@ghostfolio/ui/trend-indicator';
|
||||
import { GfValueComponent } from '@ghostfolio/ui/value';
|
||||
@ -84,7 +88,7 @@ export class GfBenchmarkComponent implements OnChanges, OnDestroy {
|
||||
}
|
||||
}
|
||||
|
||||
public onOpenBenchmarkDialog({ dataSource, symbol }: UniqueAsset) {
|
||||
public onOpenBenchmarkDialog({ dataSource, symbol }: AssetProfileIdentifier) {
|
||||
this.router.navigate([], {
|
||||
queryParams: { dataSource, symbol, benchmarkDetailDialog: true }
|
||||
});
|
||||
@ -95,7 +99,10 @@ export class GfBenchmarkComponent implements OnChanges, OnDestroy {
|
||||
this.unsubscribeSubject.complete();
|
||||
}
|
||||
|
||||
private openBenchmarkDetailDialog({ dataSource, symbol }: UniqueAsset) {
|
||||
private openBenchmarkDetailDialog({
|
||||
dataSource,
|
||||
symbol
|
||||
}: AssetProfileIdentifier) {
|
||||
const dialogRef = this.dialog.open(GfBenchmarkDetailDialogComponent, {
|
||||
data: <BenchmarkDetailDialogParams>{
|
||||
dataSource,
|
||||
|
@ -2,7 +2,10 @@ import { GfAssetProfileIconComponent } from '@ghostfolio/client/components/asset
|
||||
import { GfHoldingDetailDialogComponent } from '@ghostfolio/client/components/holding-detail-dialog/holding-detail-dialog.component';
|
||||
import { GfSymbolModule } from '@ghostfolio/client/pipes/symbol/symbol.module';
|
||||
import { getLocale } from '@ghostfolio/common/helper';
|
||||
import { PortfolioPosition, UniqueAsset } from '@ghostfolio/common/interfaces';
|
||||
import {
|
||||
AssetProfileIdentifier,
|
||||
PortfolioPosition
|
||||
} from '@ghostfolio/common/interfaces';
|
||||
import { GfNoTransactionsInfoComponent } from '@ghostfolio/ui/no-transactions-info';
|
||||
import { GfValueComponent } from '@ghostfolio/ui/value';
|
||||
|
||||
@ -102,7 +105,7 @@ export class GfHoldingsTableComponent implements OnChanges, OnDestroy, OnInit {
|
||||
}
|
||||
}
|
||||
|
||||
public onOpenHoldingDialog({ dataSource, symbol }: UniqueAsset) {
|
||||
public onOpenHoldingDialog({ dataSource, symbol }: AssetProfileIdentifier) {
|
||||
if (this.hasPermissionToOpenDetails) {
|
||||
this.router.navigate([], {
|
||||
queryParams: { dataSource, symbol, holdingDetailDialog: true }
|
||||
|
@ -1,7 +1,10 @@
|
||||
import { getTooltipOptions } from '@ghostfolio/common/chart-helper';
|
||||
import { UNKNOWN_KEY } from '@ghostfolio/common/config';
|
||||
import { getLocale, getTextColor } from '@ghostfolio/common/helper';
|
||||
import { PortfolioPosition, UniqueAsset } from '@ghostfolio/common/interfaces';
|
||||
import {
|
||||
AssetProfileIdentifier,
|
||||
PortfolioPosition
|
||||
} from '@ghostfolio/common/interfaces';
|
||||
import { ColorScheme } from '@ghostfolio/common/types';
|
||||
import { translate } from '@ghostfolio/ui/i18n';
|
||||
|
||||
@ -71,7 +74,7 @@ export class GfPortfolioProportionChartComponent
|
||||
};
|
||||
} = {};
|
||||
|
||||
@Output() proportionChartClicked = new EventEmitter<UniqueAsset>();
|
||||
@Output() proportionChartClicked = new EventEmitter<AssetProfileIdentifier>();
|
||||
|
||||
@ViewChild('chartCanvas') chartCanvas: ElementRef<HTMLCanvasElement>;
|
||||
|
||||
|
@ -1,5 +1,8 @@
|
||||
import { getAnnualizedPerformancePercent } from '@ghostfolio/common/calculation-helper';
|
||||
import { PortfolioPosition, UniqueAsset } from '@ghostfolio/common/interfaces';
|
||||
import {
|
||||
AssetProfileIdentifier,
|
||||
PortfolioPosition
|
||||
} from '@ghostfolio/common/interfaces';
|
||||
|
||||
import { CommonModule } from '@angular/common';
|
||||
import {
|
||||
@ -40,7 +43,7 @@ export class GfTreemapChartComponent
|
||||
@Input() cursor: string;
|
||||
@Input() holdings: PortfolioPosition[];
|
||||
|
||||
@Output() treemapChartClicked = new EventEmitter<UniqueAsset>();
|
||||
@Output() treemapChartClicked = new EventEmitter<AssetProfileIdentifier>();
|
||||
|
||||
@ViewChild('chartCanvas') chartCanvas: ElementRef<HTMLCanvasElement>;
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user