Feature/refactor portfolio holding response (#4649)

* Refactor portfolio holding response

* maxPrice -> marketPriceMax
* minPrice -> marketPriceMin
* orders -> activities
This commit is contained in:
Thomas Kaul 2025-05-04 09:48:43 +02:00 committed by GitHub
parent 1bced96460
commit 3646fb7f77
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 49 additions and 43 deletions

View File

@ -49,7 +49,7 @@ export class ImportService {
symbol symbol
}: AssetProfileIdentifier): Promise<Activity[]> { }: AssetProfileIdentifier): Promise<Activity[]> {
try { try {
const { firstBuyDate, historicalData, orders } = const { activities, firstBuyDate, historicalData } =
await this.portfolioService.getHolding(dataSource, undefined, symbol); await this.portfolioService.getHolding(dataSource, undefined, symbol);
const [[assetProfile], dividends] = await Promise.all([ const [[assetProfile], dividends] = await Promise.all([
@ -68,7 +68,7 @@ export class ImportService {
}) })
]); ]);
const accounts = orders const accounts = activities
.filter(({ Account }) => { .filter(({ Account }) => {
return !!Account; return !!Account;
}) })
@ -88,7 +88,7 @@ export class ImportService {
const value = new Big(quantity).mul(marketPrice).toNumber(); const value = new Big(quantity).mul(marketPrice).toNumber();
const date = parseDate(dateString); const date = parseDate(dateString);
const isDuplicate = orders.some((activity) => { const isDuplicate = activities.some((activity) => {
return ( return (
activity.accountId === Account?.id && activity.accountId === Account?.id &&
activity.SymbolProfile.currency === assetProfile.currency && activity.SymbolProfile.currency === assetProfile.currency &&

View File

@ -648,6 +648,7 @@ export class PortfolioService {
if (activities.length === 0) { if (activities.length === 0) {
return { return {
activities: [],
averagePrice: undefined, averagePrice: undefined,
dataProviderInfo: undefined, dataProviderInfo: undefined,
dividendInBaseCurrency: undefined, dividendInBaseCurrency: undefined,
@ -662,13 +663,12 @@ export class PortfolioService {
historicalData: [], historicalData: [],
investment: undefined, investment: undefined,
marketPrice: undefined, marketPrice: undefined,
maxPrice: undefined, marketPriceMax: undefined,
minPrice: undefined, marketPriceMin: undefined,
netPerformance: undefined, netPerformance: undefined,
netPerformancePercent: undefined, netPerformancePercent: undefined,
netPerformancePercentWithCurrencyEffect: undefined, netPerformancePercentWithCurrencyEffect: undefined,
netPerformanceWithCurrencyEffect: undefined, netPerformanceWithCurrencyEffect: undefined,
orders: [],
quantity: undefined, quantity: undefined,
SymbolProfile: undefined, SymbolProfile: undefined,
tags: [], tags: [],
@ -714,7 +714,7 @@ export class PortfolioService {
transactionCount transactionCount
} = position; } = position;
const activitiesOfPosition = activities.filter(({ SymbolProfile }) => { const activitiesOfHolding = activities.filter(({ SymbolProfile }) => {
return ( return (
SymbolProfile.dataSource === dataSource && SymbolProfile.dataSource === dataSource &&
SymbolProfile.symbol === symbol SymbolProfile.symbol === symbol
@ -748,12 +748,12 @@ export class PortfolioService {
); );
const historicalDataArray: HistoricalDataItem[] = []; const historicalDataArray: HistoricalDataItem[] = [];
let maxPrice = Math.max( let marketPriceMax = Math.max(
activitiesOfPosition[0].unitPriceInAssetProfileCurrency, activitiesOfHolding[0].unitPriceInAssetProfileCurrency,
marketPrice marketPrice
); );
let minPrice = Math.min( let marketPriceMin = Math.min(
activitiesOfPosition[0].unitPriceInAssetProfileCurrency, activitiesOfHolding[0].unitPriceInAssetProfileCurrency,
marketPrice marketPrice
); );
@ -793,27 +793,31 @@ export class PortfolioService {
quantity: currentQuantity quantity: currentQuantity
}); });
maxPrice = Math.max(marketPrice ?? 0, maxPrice); marketPriceMax = Math.max(marketPrice ?? 0, marketPriceMax);
minPrice = Math.min(marketPrice ?? Number.MAX_SAFE_INTEGER, minPrice); marketPriceMin = Math.min(
marketPrice ?? Number.MAX_SAFE_INTEGER,
marketPriceMin
);
} }
} else { } else {
// Add historical entry for buy date, if no historical data available // Add historical entry for buy date, if no historical data available
historicalDataArray.push({ historicalDataArray.push({
averagePrice: activitiesOfPosition[0].unitPriceInAssetProfileCurrency, averagePrice: activitiesOfHolding[0].unitPriceInAssetProfileCurrency,
date: firstBuyDate, date: firstBuyDate,
marketPrice: activitiesOfPosition[0].unitPriceInAssetProfileCurrency, marketPrice: activitiesOfHolding[0].unitPriceInAssetProfileCurrency,
quantity: activitiesOfPosition[0].quantity quantity: activitiesOfHolding[0].quantity
}); });
} }
return { return {
firstBuyDate, firstBuyDate,
marketPrice, marketPrice,
maxPrice, marketPriceMax,
minPrice, marketPriceMin,
SymbolProfile, SymbolProfile,
tags, tags,
transactionCount, transactionCount,
activities: activitiesOfHolding,
averagePrice: averagePrice.toNumber(), averagePrice: averagePrice.toNumber(),
dataProviderInfo: portfolioCalculator.getDataProviderInfos()?.[0], dataProviderInfo: portfolioCalculator.getDataProviderInfos()?.[0],
dividendInBaseCurrency: dividendInBaseCurrency.toNumber(), dividendInBaseCurrency: dividendInBaseCurrency.toNumber(),
@ -842,7 +846,6 @@ export class PortfolioService {
]?.toNumber(), ]?.toNumber(),
netPerformanceWithCurrencyEffect: netPerformanceWithCurrencyEffect:
position.netPerformanceWithCurrencyEffectMap?.['max']?.toNumber(), position.netPerformanceWithCurrencyEffectMap?.['max']?.toNumber(),
orders: activitiesOfPosition,
quantity: quantity.toNumber(), quantity: quantity.toNumber(),
value: this.exchangeRateDataService.toCurrency( value: this.exchangeRateDataService.toCurrency(
quantity.mul(marketPrice ?? 0).toNumber(), quantity.mul(marketPrice ?? 0).toNumber(),
@ -881,8 +884,8 @@ export class PortfolioService {
} }
const historicalDataArray: HistoricalDataItem[] = []; const historicalDataArray: HistoricalDataItem[] = [];
let maxPrice = marketPrice; let marketPriceMax = marketPrice;
let minPrice = marketPrice; let marketPriceMin = marketPrice;
for (const [date, { marketPrice }] of Object.entries( for (const [date, { marketPrice }] of Object.entries(
historicalData[aSymbol] historicalData[aSymbol]
@ -892,15 +895,19 @@ export class PortfolioService {
value: marketPrice value: marketPrice
}); });
maxPrice = Math.max(marketPrice ?? 0, maxPrice); marketPriceMax = Math.max(marketPrice ?? 0, marketPriceMax);
minPrice = Math.min(marketPrice ?? Number.MAX_SAFE_INTEGER, minPrice); marketPriceMin = Math.min(
marketPrice ?? Number.MAX_SAFE_INTEGER,
marketPriceMin
);
} }
return { return {
marketPrice, marketPrice,
maxPrice, marketPriceMax,
minPrice, marketPriceMin,
SymbolProfile, SymbolProfile,
activities: [],
averagePrice: 0, averagePrice: 0,
dataProviderInfo: undefined, dataProviderInfo: undefined,
dividendInBaseCurrency: 0, dividendInBaseCurrency: 0,
@ -918,7 +925,6 @@ export class PortfolioService {
netPerformancePercent: undefined, netPerformancePercent: undefined,
netPerformancePercentWithCurrencyEffect: undefined, netPerformancePercentWithCurrencyEffect: undefined,
netPerformanceWithCurrencyEffect: undefined, netPerformanceWithCurrencyEffect: undefined,
orders: [],
quantity: 0, quantity: 0,
tags: [], tags: [],
transactionCount: undefined, transactionCount: undefined,

View File

@ -105,8 +105,8 @@ export class GfHoldingDetailDialogComponent implements OnDestroy, OnInit {
public investmentPrecision = 2; public investmentPrecision = 2;
public marketDataItems: MarketData[] = []; public marketDataItems: MarketData[] = [];
public marketPrice: number; public marketPrice: number;
public maxPrice: number; public marketPriceMax: number;
public minPrice: number; public marketPriceMin: number;
public netPerformance: number; public netPerformance: number;
public netPerformancePrecision = 2; public netPerformancePrecision = 2;
public netPerformancePercent: number; public netPerformancePercent: number;
@ -234,8 +234,8 @@ export class GfHoldingDetailDialogComponent implements OnDestroy, OnInit {
historicalData, historicalData,
investment, investment,
marketPrice, marketPrice,
maxPrice, marketPriceMax,
minPrice, marketPriceMin,
netPerformance, netPerformance,
netPerformancePercent, netPerformancePercent,
netPerformancePercentWithCurrencyEffect, netPerformancePercentWithCurrencyEffect,
@ -297,8 +297,8 @@ export class GfHoldingDetailDialogComponent implements OnDestroy, OnInit {
} }
this.marketPrice = marketPrice; this.marketPrice = marketPrice;
this.maxPrice = maxPrice; this.marketPriceMax = marketPriceMax;
this.minPrice = minPrice; this.marketPriceMin = marketPriceMin;
this.netPerformance = netPerformance; this.netPerformance = netPerformance;
if ( if (

View File

@ -106,11 +106,11 @@
[locale]="data.locale" [locale]="data.locale"
[ngClass]="{ [ngClass]="{
'text-danger': 'text-danger':
minPrice?.toFixed(2) === marketPrice?.toFixed(2) && marketPriceMin?.toFixed(2) === marketPrice?.toFixed(2) &&
maxPrice?.toFixed(2) !== minPrice?.toFixed(2) marketPriceMax?.toFixed(2) !== marketPriceMin?.toFixed(2)
}" }"
[unit]="SymbolProfile?.currency" [unit]="SymbolProfile?.currency"
[value]="minPrice" [value]="marketPriceMin"
>Minimum Price</gf-value >Minimum Price</gf-value
> >
</div> </div>
@ -122,11 +122,11 @@
[locale]="data.locale" [locale]="data.locale"
[ngClass]="{ [ngClass]="{
'text-success': 'text-success':
maxPrice?.toFixed(2) === marketPrice?.toFixed(2) && marketPriceMax?.toFixed(2) === marketPrice?.toFixed(2) &&
maxPrice?.toFixed(2) !== minPrice?.toFixed(2) marketPriceMax?.toFixed(2) !== marketPriceMin?.toFixed(2)
}" }"
[unit]="SymbolProfile?.currency" [unit]="SymbolProfile?.currency"
[value]="maxPrice" [value]="marketPriceMax"
>Maximum Price</gf-value >Maximum Price</gf-value
> >
</div> </div>

View File

@ -411,8 +411,8 @@ export class DataService {
) )
.pipe( .pipe(
map((data) => { map((data) => {
if (data.orders) { if (data.activities) {
for (const order of data.orders) { for (const order of data.activities) {
order.createdAt = parseISO(order.createdAt as unknown as string); order.createdAt = parseISO(order.createdAt as unknown as string);
order.date = parseISO(order.date as unknown as string); order.date = parseISO(order.date as unknown as string);
} }

View File

@ -8,6 +8,7 @@ import {
import { Tag } from '@prisma/client'; import { Tag } from '@prisma/client';
export interface PortfolioHoldingResponse { export interface PortfolioHoldingResponse {
activities: Activity[];
averagePrice: number; averagePrice: number;
dataProviderInfo: DataProviderInfo; dataProviderInfo: DataProviderInfo;
dividendInBaseCurrency: number; dividendInBaseCurrency: number;
@ -22,13 +23,12 @@ export interface PortfolioHoldingResponse {
historicalData: HistoricalDataItem[]; historicalData: HistoricalDataItem[];
investment: number; investment: number;
marketPrice: number; marketPrice: number;
maxPrice: number; marketPriceMax: number;
minPrice: number; marketPriceMin: number;
netPerformance: number; netPerformance: number;
netPerformancePercent: number; netPerformancePercent: number;
netPerformancePercentWithCurrencyEffect: number; netPerformancePercentWithCurrencyEffect: number;
netPerformanceWithCurrencyEffect: number; netPerformanceWithCurrencyEffect: number;
orders: Activity[];
quantity: number; quantity: number;
SymbolProfile: EnhancedSymbolProfile; SymbolProfile: EnhancedSymbolProfile;
tags: Tag[]; tags: Tag[];