From 543a44a1a708a494d907e9c16ee9408c43ea021c Mon Sep 17 00:00:00 2001 From: KenTandrian Date: Sat, 3 Jan 2026 13:59:21 +0700 Subject: [PATCH] feat(api): create getCashSymbolProfiles method --- .../calculator/portfolio-calculator.ts | 1 - .../src/app/portfolio/portfolio.service.ts | 50 +++++++++++++++---- .../src/lib/models/timeline-position.ts | 4 +- 3 files changed, 41 insertions(+), 14 deletions(-) diff --git a/apps/api/src/app/portfolio/calculator/portfolio-calculator.ts b/apps/api/src/app/portfolio/calculator/portfolio-calculator.ts index ac77fd111..d2b3c0625 100644 --- a/apps/api/src/app/portfolio/calculator/portfolio-calculator.ts +++ b/apps/api/src/app/portfolio/calculator/portfolio-calculator.ts @@ -405,7 +405,6 @@ export abstract class PortfolioCalculator { feeInBaseCurrency, timeWeightedInvestment, timeWeightedInvestmentWithCurrencyEffect, - assetSubClass: item.assetSubClass, dividend: totalDividend, dividendInBaseCurrency: totalDividendInBaseCurrency, averagePrice: item.averagePrice, diff --git a/apps/api/src/app/portfolio/portfolio.service.ts b/apps/api/src/app/portfolio/portfolio.service.ts index cc190ca1a..ca22642a5 100644 --- a/apps/api/src/app/portfolio/portfolio.service.ts +++ b/apps/api/src/app/portfolio/portfolio.service.ts @@ -553,6 +553,9 @@ export class PortfolioService { assetProfileIdentifiers ); + const cashSymbolProfiles = this.getCashSymbolProfiles(cashDetails); + symbolProfiles.push(...cashSymbolProfiles); + const symbolProfileMap: { [symbol: string]: EnhancedSymbolProfile } = {}; for (const symbolProfile of symbolProfiles) { symbolProfileMap[symbolProfile.symbol] = symbolProfile; @@ -564,7 +567,6 @@ export class PortfolioService { } for (const { - assetSubClass, currency, dividend, firstBuyDate, @@ -608,7 +610,6 @@ export class PortfolioService { } holdings[symbol] = { - assetSubClass, currency, markets, marketsAdvanced, @@ -619,9 +620,10 @@ export class PortfolioService { allocationInPercentage: filteredValueInBaseCurrency.eq(0) ? 0 : valueInBaseCurrency.div(filteredValueInBaseCurrency).toNumber(), - assetClass: assetProfile?.assetClass, - countries: assetProfile?.countries, - dataSource: assetProfile?.dataSource, + assetClass: assetProfile.assetClass, + assetSubClass: assetProfile.assetSubClass, + countries: assetProfile.countries, + dataSource: assetProfile.dataSource, dateOfFirstActivity: parseDate(firstBuyDate), dividend: dividend?.toNumber() ?? 0, grossPerformance: grossPerformance?.toNumber() ?? 0, @@ -631,7 +633,7 @@ export class PortfolioService { grossPerformanceWithCurrencyEffect: grossPerformanceWithCurrencyEffect?.toNumber() ?? 0, holdings: - assetProfile?.holdings.map(({ allocationInPercentage, name }) => { + assetProfile.holdings.map(({ allocationInPercentage, name }) => { return { allocationInPercentage, name, @@ -641,8 +643,7 @@ export class PortfolioService { }; }) ?? [], investment: investment.toNumber(), - name: - assetSubClass === AssetSubClass.CASH ? currency : assetProfile?.name, + name: assetProfile.name, netPerformance: netPerformance?.toNumber() ?? 0, netPerformancePercent: netPerformancePercentage?.toNumber() ?? 0, netPerformancePercentWithCurrencyEffect: @@ -652,8 +653,8 @@ export class PortfolioService { netPerformanceWithCurrencyEffect: netPerformanceWithCurrencyEffectMap?.[dateRange]?.toNumber() ?? 0, quantity: quantity.toNumber(), - sectors: assetProfile?.sectors, - url: assetProfile?.url, + sectors: assetProfile.sectors, + url: assetProfile.url, valueInBaseCurrency: valueInBaseCurrency.toNumber() }; } @@ -1533,6 +1534,35 @@ export class PortfolioService { return cashPositions; } + private getCashSymbolProfiles(cashDetails: CashDetails) { + const cashSymbols = [ + ...new Set(cashDetails.accounts.map(({ currency }) => currency)) + ]; + + return cashSymbols.map((currency) => { + const account = cashDetails.accounts.find( + ({ currency: accountCurrency }) => accountCurrency === currency + ); + + return { + currency, + activitiesCount: 0, + assetClass: AssetClass.LIQUIDITY, + assetSubClass: AssetSubClass.CASH, + countries: [], + createdAt: account.createdAt, + dataSource: DataSource.MANUAL, + holdings: [], + id: currency, + isActive: true, + name: currency, + sectors: [], + symbol: currency, + updatedAt: account.updatedAt + }; + }); + } + private getDividendsByGroup({ dividends, groupBy diff --git a/libs/common/src/lib/models/timeline-position.ts b/libs/common/src/lib/models/timeline-position.ts index 0dce72b21..f683c0951 100644 --- a/libs/common/src/lib/models/timeline-position.ts +++ b/libs/common/src/lib/models/timeline-position.ts @@ -4,13 +4,11 @@ import { } from '@ghostfolio/common/class-transformer'; import { DateRange } from '@ghostfolio/common/types'; -import { AssetSubClass, DataSource, Tag } from '@prisma/client'; +import { DataSource, Tag } from '@prisma/client'; import { Big } from 'big.js'; import { Transform, Type } from 'class-transformer'; export class TimelinePosition { - assetSubClass: AssetSubClass; - @Transform(transformToBig, { toClassOnly: true }) @Type(() => Big) averagePrice: Big;