Browse Source

Task/refactor getHolding() in portfolio service (#5898)

* Refactor getHolding() if no holding has been found

* Update changelog
pull/5931/head
Thomas Kaul 3 weeks ago
committed by GitHub
parent
commit
9f878c42f4
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 6
      CHANGELOG.md
  2. 9
      apps/api/src/app/import/import.service.ts
  3. 141
      apps/api/src/app/portfolio/portfolio.service.ts

6
CHANGELOG.md

@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## Unreleased
### Changed
- Refactored the get holding functionality in the portfolio service
## 2.216.0 - 2025-11-10 ## 2.216.0 - 2025-11-10
### Changed ### Changed

9
apps/api/src/app/import/import.service.ts

@ -58,14 +58,19 @@ export class ImportService {
userId userId
}: AssetProfileIdentifier & { userId: string }): Promise<Activity[]> { }: AssetProfileIdentifier & { userId: string }): Promise<Activity[]> {
try { try {
const { activities, firstBuyDate, historicalData } = const holding = await this.portfolioService.getHolding({
await this.portfolioService.getHolding({
dataSource, dataSource,
symbol, symbol,
userId, userId,
impersonationId: undefined impersonationId: undefined
}); });
if (!holding) {
return [];
}
const { activities, firstBuyDate, historicalData } = holding;
const [[assetProfile], dividends] = await Promise.all([ const [[assetProfile], dividends] = await Promise.all([
this.symbolProfileService.getSymbolProfiles([ this.symbolProfileService.getSymbolProfiles([
{ {

141
apps/api/src/app/portfolio/portfolio.service.ts

@ -88,7 +88,6 @@ import {
parseISO, parseISO,
set set
} from 'date-fns'; } from 'date-fns';
import { isEmpty } from 'lodash';
import { PortfolioCalculator } from './calculator/portfolio-calculator'; import { PortfolioCalculator } from './calculator/portfolio-calculator';
import { PortfolioCalculatorFactory } from './calculator/portfolio-calculator.factory'; import { PortfolioCalculatorFactory } from './calculator/portfolio-calculator.factory';
@ -776,35 +775,7 @@ export class PortfolioService {
}); });
if (activities.length === 0) { if (activities.length === 0) {
return { return undefined;
activities: [],
activitiesCount: 0,
averagePrice: undefined,
dataProviderInfo: undefined,
dividendInBaseCurrency: undefined,
dividendYieldPercent: undefined,
dividendYieldPercentWithCurrencyEffect: undefined,
feeInBaseCurrency: undefined,
firstBuyDate: undefined,
grossPerformance: undefined,
grossPerformancePercent: undefined,
grossPerformancePercentWithCurrencyEffect: undefined,
grossPerformanceWithCurrencyEffect: undefined,
historicalData: [],
investmentInBaseCurrencyWithCurrencyEffect: undefined,
marketPrice: undefined,
marketPriceMax: undefined,
marketPriceMin: undefined,
netPerformance: undefined,
netPerformancePercent: undefined,
netPerformancePercentWithCurrencyEffect: undefined,
netPerformanceWithCurrencyEffect: undefined,
performances: undefined,
quantity: undefined,
SymbolProfile: undefined,
tags: [],
value: undefined
};
} }
const [SymbolProfile] = await this.symbolProfileService.getSymbolProfiles([ const [SymbolProfile] = await this.symbolProfileService.getSymbolProfiles([
@ -818,7 +789,6 @@ export class PortfolioService {
currency: userCurrency currency: userCurrency
}); });
const portfolioStart = portfolioCalculator.getStartDate();
const transactionPoints = portfolioCalculator.getTransactionPoints(); const transactionPoints = portfolioCalculator.getTransactionPoints();
const { positions } = await portfolioCalculator.getSnapshot(); const { positions } = await portfolioCalculator.getSnapshot();
@ -827,7 +797,10 @@ export class PortfolioService {
return position.dataSource === dataSource && position.symbol === symbol; return position.dataSource === dataSource && position.symbol === symbol;
}); });
if (holding) { if (!holding) {
return undefined;
}
const { const {
averagePrice, averagePrice,
currency, currency,
@ -868,13 +841,9 @@ export class PortfolioService {
const dividendYieldPercentWithCurrencyEffect = const dividendYieldPercentWithCurrencyEffect =
getAnnualizedPerformancePercent({ getAnnualizedPerformancePercent({
daysInMarket: differenceInDays(new Date(), parseDate(firstBuyDate)), daysInMarket: differenceInDays(new Date(), parseDate(firstBuyDate)),
netPerformancePercentage: timeWeightedInvestmentWithCurrencyEffect.eq( netPerformancePercentage: timeWeightedInvestmentWithCurrencyEffect.eq(0)
0
)
? new Big(0) ? new Big(0)
: dividendInBaseCurrency.div( : dividendInBaseCurrency.div(timeWeightedInvestmentWithCurrencyEffect)
timeWeightedInvestmentWithCurrencyEffect
)
}); });
const historicalData = await this.dataProviderService.getHistorical( const historicalData = await this.dataProviderService.getHistorical(
@ -928,9 +897,7 @@ export class PortfolioService {
date, date,
averagePrice: currentAveragePrice, averagePrice: currentAveragePrice,
marketPrice: marketPrice:
historicalDataArray.length > 0 historicalDataArray.length > 0 ? marketPrice : currentAveragePrice,
? marketPrice
: currentAveragePrice,
quantity: currentQuantity quantity: currentQuantity
}); });
@ -1007,98 +974,6 @@ export class PortfolioService {
userCurrency userCurrency
) )
}; };
} else {
const currentData = await this.dataProviderService.getQuotes({
user,
items: [{ symbol, dataSource: DataSource.YAHOO }]
});
const marketPrice = currentData[symbol]?.marketPrice;
let historicalData = await this.dataProviderService.getHistorical(
[{ symbol, dataSource: DataSource.YAHOO }],
'day',
portfolioStart,
new Date()
);
if (isEmpty(historicalData)) {
try {
historicalData = await this.dataProviderService.getHistoricalRaw({
assetProfileIdentifiers: [{ symbol, dataSource: DataSource.YAHOO }],
from: portfolioStart,
to: new Date()
});
} catch {
historicalData = {
[symbol]: {}
};
}
}
const historicalDataArray: HistoricalDataItem[] = [];
let marketPriceMax = marketPrice;
let marketPriceMaxDate = new Date();
let marketPriceMin = marketPrice;
for (const [date, { marketPrice }] of Object.entries(
historicalData[symbol]
)) {
historicalDataArray.push({
date,
value: marketPrice
});
if (marketPrice > marketPriceMax) {
marketPriceMax = marketPrice;
marketPriceMaxDate = parseISO(date);
}
marketPriceMin = Math.min(
marketPrice ?? Number.MAX_SAFE_INTEGER,
marketPriceMin
);
}
const performancePercent =
this.benchmarkService.calculateChangeInPercentage(
marketPriceMax,
marketPrice
);
return {
marketPrice,
marketPriceMax,
marketPriceMin,
SymbolProfile,
activities: [],
activitiesCount: 0,
averagePrice: 0,
dataProviderInfo: undefined,
dividendInBaseCurrency: 0,
dividendYieldPercent: 0,
dividendYieldPercentWithCurrencyEffect: 0,
feeInBaseCurrency: 0,
firstBuyDate: undefined,
grossPerformance: undefined,
grossPerformancePercent: undefined,
grossPerformancePercentWithCurrencyEffect: undefined,
grossPerformanceWithCurrencyEffect: undefined,
historicalData: historicalDataArray,
investmentInBaseCurrencyWithCurrencyEffect: 0,
netPerformance: undefined,
netPerformancePercent: undefined,
netPerformancePercentWithCurrencyEffect: undefined,
netPerformanceWithCurrencyEffect: undefined,
performances: {
allTimeHigh: {
performancePercent,
date: marketPriceMaxDate
}
},
quantity: 0,
tags: [],
value: 0
};
}
} }
public async getPerformance({ public async getPerformance({

Loading…
Cancel
Save