diff --git a/CHANGELOG.md b/CHANGELOG.md index 0372027d4..198428aad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +### Added + +- Introduced the allocations by platform chart on the allocations page + ### Changed - Deprecated the use of the environment variable `BASE_CURRENCY` diff --git a/apps/api/src/app/portfolio/portfolio.controller.ts b/apps/api/src/app/portfolio/portfolio.controller.ts index d20e3bf87..8f9c2e579 100644 --- a/apps/api/src/app/portfolio/portfolio.controller.ts +++ b/apps/api/src/app/portfolio/portfolio.controller.ts @@ -91,6 +91,7 @@ export class PortfolioController { filteredValueInPercentage, hasErrors, holdings, + platforms, summary, totalValueInBaseCurrency } = await this.portfolioService.getDetails({ @@ -139,6 +140,10 @@ export class PortfolioController { for (const [name, { valueInBaseCurrency }] of Object.entries(accounts)) { accounts[name].valueInPercentage = valueInBaseCurrency / totalValue; } + + for (const [name, { valueInBaseCurrency }] of Object.entries(platforms)) { + platforms[name].valueInPercentage = valueInBaseCurrency / totalValue; + } } if ( @@ -181,6 +186,7 @@ export class PortfolioController { filteredValueInPercentage, hasError, holdings, + platforms, totalValueInBaseCurrency, summary: portfolioSummary }; diff --git a/apps/api/src/app/portfolio/portfolio.service.ts b/apps/api/src/app/portfolio/portfolio.service.ts index 6db73cebf..fd77baf77 100644 --- a/apps/api/src/app/portfolio/portfolio.service.ts +++ b/apps/api/src/app/portfolio/portfolio.service.ts @@ -583,7 +583,7 @@ export class PortfolioService { } } - const accounts = await this.getValueOfAccounts({ + const { accounts, platforms } = await this.getValueOfAccountsAndPlatforms({ filters, orders, portfolioItemsNow, @@ -641,6 +641,7 @@ export class PortfolioService { return { accounts, holdings, + platforms, summary, filteredValueInBaseCurrency: filteredValueInBaseCurrency.toNumber(), filteredValueInPercentage: summary.netWorth @@ -1171,7 +1172,7 @@ export class PortfolioService { portfolioItemsNow[position.symbol] = position; } - const accounts = await this.getValueOfAccounts({ + const { accounts } = await this.getValueOfAccountsAndPlatforms({ orders, portfolioItemsNow, userCurrency, @@ -1691,7 +1692,7 @@ export class PortfolioService { }; } - private async getValueOfAccounts({ + private async getValueOfAccountsAndPlatforms({ filters = [], orders, portfolioItemsNow, @@ -1715,6 +1716,7 @@ export class PortfolioService { }); const accounts: PortfolioDetails['accounts'] = {}; + const platforms: PortfolioDetails['platforms'] = {}; let currentAccounts: (Account & { Order?: Order[]; @@ -1767,6 +1769,26 @@ export class PortfolioService { ) }; + if (platforms[account.Platform?.id || UNKNOWN_KEY]?.valueInBaseCurrency) { + platforms[account.Platform?.id || UNKNOWN_KEY].valueInBaseCurrency += + this.exchangeRateDataService.toCurrency( + account.balance, + account.currency, + userCurrency + ); + } else { + platforms[account.Platform?.id || UNKNOWN_KEY] = { + balance: account.balance, + currency: account.currency, + name: account.Platform?.name, + valueInBaseCurrency: this.exchangeRateDataService.toCurrency( + account.balance, + account.currency, + userCurrency + ) + }; + } + for (const order of ordersByAccount) { let currentValueOfSymbolInBaseCurrency = order.quantity * @@ -1789,10 +1811,26 @@ export class PortfolioService { valueInBaseCurrency: currentValueOfSymbolInBaseCurrency }; } + + if ( + platforms[order.Account?.Platform?.id || UNKNOWN_KEY] + ?.valueInBaseCurrency + ) { + platforms[ + order.Account?.Platform?.id || UNKNOWN_KEY + ].valueInBaseCurrency += currentValueOfSymbolInBaseCurrency; + } else { + platforms[order.Account?.Platform?.id || UNKNOWN_KEY] = { + balance: 0, + currency: order.Account?.currency, + name: account.Platform?.name, + valueInBaseCurrency: currentValueOfSymbolInBaseCurrency + }; + } } } - return accounts; + return { accounts, platforms }; } private async getUserId(aImpersonationId: string, aUserId: string) { diff --git a/apps/client/src/app/pages/portfolio/allocations/allocations-page.component.ts b/apps/client/src/app/pages/portfolio/allocations/allocations-page.component.ts index 6b5baa3ef..8d89f41a0 100644 --- a/apps/client/src/app/pages/portfolio/allocations/allocations-page.component.ts +++ b/apps/client/src/app/pages/portfolio/allocations/allocations-page.component.ts @@ -20,7 +20,7 @@ import { import { hasPermission, permissions } from '@ghostfolio/common/permissions'; import { Market } from '@ghostfolio/common/types'; import { translate } from '@ghostfolio/ui/i18n'; -import { Account, AssetClass, DataSource } from '@prisma/client'; +import { Account, AssetClass, DataSource, Platform } from '@prisma/client'; import { isNumber } from 'lodash'; import { DeviceDetectorService } from 'ngx-device-detector'; import { Subject } from 'rxjs'; @@ -55,6 +55,12 @@ export class AllocationsPageComponent implements OnDestroy, OnInit { [key in Market]: { name: string; value: number }; }; public placeholder = ''; + public platforms: { + [id: string]: Pick & { + id: string; + value: number; + }; + }; public portfolioDetails: PortfolioDetails; public positions: { [symbol: string]: Pick< @@ -230,6 +236,7 @@ export class AllocationsPageComponent implements OnDestroy, OnInit { value: undefined } }; + this.platforms = {}; this.positions = {}; this.sectors = { [UNKNOWN_KEY]: { @@ -371,6 +378,25 @@ export class AllocationsPageComponent implements OnDestroy, OnInit { }; } + for (const [ + id, + { name, valueInBaseCurrency, valueInPercentage } + ] of Object.entries(this.portfolioDetails.platforms)) { + let value = 0; + + if (this.hasImpersonationId) { + value = valueInPercentage; + } else { + value = valueInBaseCurrency; + } + + this.platforms[id] = { + id, + name, + value + }; + } + const marketsTotal = this.markets.developedMarkets.value + this.markets.emergingMarkets.value + diff --git a/apps/client/src/app/pages/portfolio/allocations/allocations-page.html b/apps/client/src/app/pages/portfolio/allocations/allocations-page.html index e618a3db5..ab78c1930 100644 --- a/apps/client/src/app/pages/portfolio/allocations/allocations-page.html +++ b/apps/client/src/app/pages/portfolio/allocations/allocations-page.html @@ -38,18 +38,18 @@
- By Account + By Platform @@ -250,6 +250,25 @@
+
+ + + By Account + + + + + +
diff --git a/libs/common/src/lib/interfaces/portfolio-details.interface.ts b/libs/common/src/lib/interfaces/portfolio-details.interface.ts index 2fd07dc71..565c17c7d 100644 --- a/libs/common/src/lib/interfaces/portfolio-details.interface.ts +++ b/libs/common/src/lib/interfaces/portfolio-details.interface.ts @@ -16,6 +16,15 @@ export interface PortfolioDetails { filteredValueInBaseCurrency?: number; filteredValueInPercentage: number; holdings: { [symbol: string]: PortfolioPosition }; + platforms: { + [id: string]: { + balance: number; + currency: string; + name: string; + valueInBaseCurrency: number; + valueInPercentage?: number; + }; + }; summary: PortfolioSummary; totalValueInBaseCurrency?: number; }