Browse Source

Feature/refactor portfolio service (#5063)

* Refactor portfolio service
pull/5066/head
Thomas Kaul 1 day ago
committed by GitHub
parent
commit
7a97ec75f4
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 3
      apps/api/src/app/import/import.controller.ts
  2. 12
      apps/api/src/app/import/import.service.ts
  3. 36
      apps/api/src/app/portfolio/portfolio.controller.ts
  4. 102
      apps/api/src/app/portfolio/portfolio.service.ts

3
apps/api/src/app/import/import.controller.ts

@ -100,7 +100,8 @@ export class ImportController {
): Promise<ImportResponse> {
const activities = await this.importService.getDividends({
dataSource,
symbol
symbol,
userId: this.request.user.id
});
return { activities };

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

@ -48,11 +48,17 @@ export class ImportService {
public async getDividends({
dataSource,
symbol
}: AssetProfileIdentifier): Promise<Activity[]> {
symbol,
userId
}: AssetProfileIdentifier & { userId: string }): Promise<Activity[]> {
try {
const { activities, firstBuyDate, historicalData } =
await this.portfolioService.getHolding(dataSource, undefined, symbol);
await this.portfolioService.getHolding({
dataSource,
symbol,
userId,
impersonationId: undefined
});
const [[assetProfile], dividends] = await Promise.all([
this.symbolProfileService.getSymbolProfiles([

36
apps/api/src/app/portfolio/portfolio.controller.ts

@ -375,11 +375,12 @@ export class PortfolioController {
@Param('dataSource') dataSource: DataSource,
@Param('symbol') symbol: string
): Promise<PortfolioHoldingResponse> {
const holding = await this.portfolioService.getHolding(
const holding = await this.portfolioService.getHolding({
dataSource,
impersonationId,
symbol
);
symbol,
userId: this.request.user.id
});
if (!holding) {
throw new HttpException(
@ -453,7 +454,8 @@ export class PortfolioController {
filters,
groupBy,
impersonationId,
savingsRate: this.request.user?.Settings?.settings.savingsRate
savingsRate: this.request.user?.Settings?.settings.savingsRate,
userId: this.request.user.id
});
if (
@ -622,11 +624,12 @@ export class PortfolioController {
@Param('dataSource') dataSource: DataSource,
@Param('symbol') symbol: string
): Promise<PortfolioHoldingResponse> {
const holding = await this.portfolioService.getHolding(
const holding = await this.portfolioService.getHolding({
dataSource,
impersonationId,
symbol
);
symbol,
userId: this.request.user.id
});
if (!holding) {
throw new HttpException(
@ -643,7 +646,10 @@ export class PortfolioController {
public async getReport(
@Headers(HEADER_KEY_IMPERSONATION.toLowerCase()) impersonationId: string
): Promise<PortfolioReportResponse> {
const report = await this.portfolioService.getReport(impersonationId);
const report = await this.portfolioService.getReport({
impersonationId,
userId: this.request.user.id
});
if (
this.configurationService.get('ENABLE_FEATURE_SUBSCRIPTION') &&
@ -672,11 +678,12 @@ export class PortfolioController {
@Param('dataSource') dataSource: DataSource,
@Param('symbol') symbol: string
): Promise<void> {
const holding = await this.portfolioService.getHolding(
const holding = await this.portfolioService.getHolding({
dataSource,
impersonationId,
symbol
);
symbol,
userId: this.request.user.id
});
if (!holding) {
throw new HttpException(
@ -707,11 +714,12 @@ export class PortfolioController {
@Param('dataSource') dataSource: DataSource,
@Param('symbol') symbol: string
): Promise<void> {
const holding = await this.portfolioService.getHolding(
const holding = await this.portfolioService.getHolding({
dataSource,
impersonationId,
symbol
);
symbol,
userId: this.request.user.id
});
if (!holding) {
throw new HttpException(

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

@ -312,15 +312,17 @@ export class PortfolioService {
filters,
groupBy,
impersonationId,
savingsRate
savingsRate,
userId
}: {
dateRange: DateRange;
filters?: Filter[];
groupBy?: GroupBy;
impersonationId: string;
savingsRate: number;
userId: string;
}): Promise<PortfolioInvestments> {
const userId = await this.getUserId(impersonationId, this.request.user.id);
userId = await this.getUserId(impersonationId, userId);
const user = await this.userService.user({ id: userId });
const userCurrency = this.getUserCurrency(user);
@ -676,12 +678,18 @@ export class PortfolioService {
};
}
public async getHolding(
aDataSource: DataSource,
aImpersonationId: string,
aSymbol: string
): Promise<PortfolioHoldingResponse> {
const userId = await this.getUserId(aImpersonationId, this.request.user.id);
public async getHolding({
dataSource,
impersonationId,
symbol,
userId
}: {
dataSource: DataSource;
impersonationId: string;
symbol: string;
userId: string;
}): Promise<PortfolioHoldingResponse> {
userId = await this.getUserId(impersonationId, userId);
const user = await this.userService.user({ id: userId });
const userCurrency = this.getUserCurrency(user);
@ -724,7 +732,7 @@ export class PortfolioService {
}
const [SymbolProfile] = await this.symbolProfileService.getSymbolProfiles([
{ dataSource: aDataSource, symbol: aSymbol }
{ dataSource, symbol }
]);
const portfolioCalculator = this.calculatorFactory.createCalculator({
@ -739,26 +747,33 @@ export class PortfolioService {
const { positions } = await portfolioCalculator.getSnapshot();
const position = positions.find(({ dataSource, symbol }) => {
return dataSource === aDataSource && symbol === aSymbol;
const holding = positions.find((position) => {
return position.dataSource === dataSource && position.symbol === symbol;
});
if (position) {
if (holding) {
const {
averagePrice,
currency,
dataSource,
dividendInBaseCurrency,
fee,
firstBuyDate,
grossPerformance,
grossPerformancePercentage,
grossPerformancePercentageWithCurrencyEffect,
grossPerformanceWithCurrencyEffect,
investmentWithCurrencyEffect,
marketPrice,
netPerformance,
netPerformancePercentage,
netPerformancePercentageWithCurrencyEffectMap,
netPerformanceWithCurrencyEffectMap,
quantity,
symbol,
tags,
timeWeightedInvestment,
timeWeightedInvestmentWithCurrencyEffect,
transactionCount
} = position;
} = holding;
const activitiesOfHolding = activities.filter(({ SymbolProfile }) => {
return (
@ -787,7 +802,7 @@ export class PortfolioService {
});
const historicalData = await this.dataProviderService.getHistorical(
[{ dataSource, symbol: aSymbol }],
[{ dataSource, symbol }],
'day',
parseISO(firstBuyDate),
new Date()
@ -807,10 +822,10 @@ export class PortfolioService {
marketPrice
);
if (historicalData[aSymbol]) {
if (historicalData[symbol]) {
let j = -1;
for (const [date, { marketPrice }] of Object.entries(
historicalData[aSymbol]
historicalData[symbol]
)) {
while (
j + 1 < transactionPoints.length &&
@ -823,8 +838,8 @@ export class PortfolioService {
let currentQuantity = 0;
const currentSymbol = transactionPoints[j]?.items.find(
({ symbol }) => {
return symbol === aSymbol;
(transactionPointSymbol) => {
return transactionPointSymbol.symbol === symbol;
}
);
@ -888,24 +903,21 @@ export class PortfolioService {
SymbolProfile.currency,
userCurrency
),
grossPerformance: position.grossPerformance?.toNumber(),
grossPerformancePercent:
position.grossPerformancePercentage?.toNumber(),
grossPerformance: grossPerformance?.toNumber(),
grossPerformancePercent: grossPerformancePercentage?.toNumber(),
grossPerformancePercentWithCurrencyEffect:
position.grossPerformancePercentageWithCurrencyEffect?.toNumber(),
grossPerformancePercentageWithCurrencyEffect?.toNumber(),
grossPerformanceWithCurrencyEffect:
position.grossPerformanceWithCurrencyEffect?.toNumber(),
grossPerformanceWithCurrencyEffect?.toNumber(),
historicalData: historicalDataArray,
investmentInBaseCurrencyWithCurrencyEffect:
position.investmentWithCurrencyEffect?.toNumber(),
netPerformance: position.netPerformance?.toNumber(),
netPerformancePercent: position.netPerformancePercentage?.toNumber(),
investmentWithCurrencyEffect?.toNumber(),
netPerformance: netPerformance?.toNumber(),
netPerformancePercent: netPerformancePercentage?.toNumber(),
netPerformancePercentWithCurrencyEffect:
position.netPerformancePercentageWithCurrencyEffectMap?.[
'max'
]?.toNumber(),
netPerformancePercentageWithCurrencyEffectMap?.['max']?.toNumber(),
netPerformanceWithCurrencyEffect:
position.netPerformanceWithCurrencyEffectMap?.['max']?.toNumber(),
netPerformanceWithCurrencyEffectMap?.['max']?.toNumber(),
performances: {
allTimeHigh: {
performancePercent,
@ -922,12 +934,12 @@ export class PortfolioService {
} else {
const currentData = await this.dataProviderService.getQuotes({
user,
items: [{ dataSource: DataSource.YAHOO, symbol: aSymbol }]
items: [{ symbol, dataSource: DataSource.YAHOO }]
});
const marketPrice = currentData[aSymbol]?.marketPrice;
const marketPrice = currentData[symbol]?.marketPrice;
let historicalData = await this.dataProviderService.getHistorical(
[{ dataSource: DataSource.YAHOO, symbol: aSymbol }],
[{ symbol, dataSource: DataSource.YAHOO }],
'day',
portfolioStart,
new Date()
@ -936,15 +948,13 @@ export class PortfolioService {
if (isEmpty(historicalData)) {
try {
historicalData = await this.dataProviderService.getHistoricalRaw({
assetProfileIdentifiers: [
{ dataSource: DataSource.YAHOO, symbol: aSymbol }
],
assetProfileIdentifiers: [{ symbol, dataSource: DataSource.YAHOO }],
from: portfolioStart,
to: new Date()
});
} catch {
historicalData = {
[aSymbol]: {}
[symbol]: {}
};
}
}
@ -955,7 +965,7 @@ export class PortfolioService {
let marketPriceMin = marketPrice;
for (const [date, { marketPrice }] of Object.entries(
historicalData[aSymbol]
historicalData[symbol]
)) {
historicalDataArray.push({
date,
@ -1116,10 +1126,14 @@ export class PortfolioService {
};
}
public async getReport(
impersonationId: string
): Promise<PortfolioReportResponse> {
const userId = await this.getUserId(impersonationId, this.request.user.id);
public async getReport({
impersonationId,
userId
}: {
impersonationId: string;
userId: string;
}): Promise<PortfolioReportResponse> {
userId = await this.getUserId(impersonationId, userId);
const userSettings = this.request.user.Settings.settings as UserSettings;
const { accounts, holdings, markets, marketsAdvanced, summary } =

Loading…
Cancel
Save