diff --git a/CHANGELOG.md b/CHANGELOG.md index 954df7f54..0372027d4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - Deprecated the use of the environment variable `BASE_CURRENCY` +- Cleaned up initial values from the _X-ray_ section ## 1.263.0 - 2023-04-30 diff --git a/apps/api/src/app/portfolio/portfolio.controller.ts b/apps/api/src/app/portfolio/portfolio.controller.ts index 299db2a1b..d20e3bf87 100644 --- a/apps/api/src/app/portfolio/portfolio.controller.ts +++ b/apps/api/src/app/portfolio/portfolio.controller.ts @@ -136,9 +136,8 @@ export class PortfolioController { portfolioPosition.value / totalValue; } - for (const [name, { current, original }] of Object.entries(accounts)) { - accounts[name].current = current / totalValue; - accounts[name].original = original / totalInvestment; + for (const [name, { valueInBaseCurrency }] of Object.entries(accounts)) { + accounts[name].valueInPercentage = valueInBaseCurrency / totalValue; } } diff --git a/apps/api/src/app/portfolio/portfolio.service.ts b/apps/api/src/app/portfolio/portfolio.service.ts index 5c995c182..6db73cebf 100644 --- a/apps/api/src/app/portfolio/portfolio.service.ts +++ b/apps/api/src/app/portfolio/portfolio.service.ts @@ -7,12 +7,9 @@ import { PortfolioOrder } from '@ghostfolio/api/app/portfolio/interfaces/portfol import { TransactionPoint } from '@ghostfolio/api/app/portfolio/interfaces/transaction-point.interface'; import { UserService } from '@ghostfolio/api/app/user/user.service'; import { AccountClusterRiskCurrentInvestment } from '@ghostfolio/api/models/rules/account-cluster-risk/current-investment'; -import { AccountClusterRiskInitialInvestment } from '@ghostfolio/api/models/rules/account-cluster-risk/initial-investment'; import { AccountClusterRiskSingleAccount } from '@ghostfolio/api/models/rules/account-cluster-risk/single-account'; import { CurrencyClusterRiskBaseCurrencyCurrentInvestment } from '@ghostfolio/api/models/rules/currency-cluster-risk/base-currency-current-investment'; -import { CurrencyClusterRiskBaseCurrencyInitialInvestment } from '@ghostfolio/api/models/rules/currency-cluster-risk/base-currency-initial-investment'; import { CurrencyClusterRiskCurrentInvestment } from '@ghostfolio/api/models/rules/currency-cluster-risk/current-investment'; -import { CurrencyClusterRiskInitialInvestment } from '@ghostfolio/api/models/rules/currency-cluster-risk/initial-investment'; import { FeeRatioInitialInvestment } from '@ghostfolio/api/models/rules/fees/fee-ratio-initial-investment'; import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; import { DataProviderService } from '@ghostfolio/api/services/data-provider/data-provider.service'; @@ -149,7 +146,8 @@ export class PortfolioService { } } - const valueInBaseCurrency = details.accounts[account.id]?.current ?? 0; + const valueInBaseCurrency = + details.accounts[account.id]?.valueInBaseCurrency ?? 0; const result = { ...account, @@ -618,9 +616,8 @@ export class PortfolioService { accounts[UNKNOWN_KEY] = { balance: 0, currency: userCurrency, - current: emergencyFundInCash, name: UNKNOWN_KEY, - original: emergencyFundInCash + valueInBaseCurrency: emergencyFundInCash }; holdings[userCurrency] = { @@ -1185,10 +1182,6 @@ export class PortfolioService { rules: { accountClusterRisk: await this.rulesService.evaluate( [ - new AccountClusterRiskInitialInvestment( - this.exchangeRateDataService, - accounts - ), new AccountClusterRiskCurrentInvestment( this.exchangeRateDataService, accounts @@ -1202,18 +1195,10 @@ export class PortfolioService { ), currencyClusterRisk: await this.rulesService.evaluate( [ - new CurrencyClusterRiskBaseCurrencyInitialInvestment( - this.exchangeRateDataService, - positions - ), new CurrencyClusterRiskBaseCurrencyCurrentInvestment( this.exchangeRateDataService, positions ), - new CurrencyClusterRiskInitialInvestment( - this.exchangeRateDataService, - positions - ), new CurrencyClusterRiskCurrentInvestment( this.exchangeRateDataService, positions @@ -1774,13 +1759,8 @@ export class PortfolioService { accounts[account.id] = { balance: account.balance, currency: account.currency, - current: this.exchangeRateDataService.toCurrency( - account.balance, - account.currency, - userCurrency - ), name: account.name, - original: this.exchangeRateDataService.toCurrency( + valueInBaseCurrency: this.exchangeRateDataService.toCurrency( account.balance, account.currency, userCurrency @@ -1793,30 +1773,20 @@ export class PortfolioService { (portfolioItemsNow[order.SymbolProfile.symbol]?.marketPrice ?? order.unitPrice ?? 0); - let originalValueOfSymbolInBaseCurrency = - this.exchangeRateDataService.toCurrency( - order.quantity * order.unitPrice, - order.SymbolProfile.currency, - userCurrency - ); if (order.type === 'SELL') { currentValueOfSymbolInBaseCurrency *= -1; - originalValueOfSymbolInBaseCurrency *= -1; } - if (accounts[order.Account?.id || UNKNOWN_KEY]?.current) { - accounts[order.Account?.id || UNKNOWN_KEY].current += + if (accounts[order.Account?.id || UNKNOWN_KEY]?.valueInBaseCurrency) { + accounts[order.Account?.id || UNKNOWN_KEY].valueInBaseCurrency += currentValueOfSymbolInBaseCurrency; - accounts[order.Account?.id || UNKNOWN_KEY].original += - originalValueOfSymbolInBaseCurrency; } else { accounts[order.Account?.id || UNKNOWN_KEY] = { balance: 0, currency: order.Account?.currency, - current: currentValueOfSymbolInBaseCurrency, name: account.name, - original: originalValueOfSymbolInBaseCurrency + valueInBaseCurrency: currentValueOfSymbolInBaseCurrency }; } } diff --git a/apps/api/src/models/rules/account-cluster-risk/current-investment.ts b/apps/api/src/models/rules/account-cluster-risk/current-investment.ts index 7a6d64809..d0cdbb58c 100644 --- a/apps/api/src/models/rules/account-cluster-risk/current-investment.ts +++ b/apps/api/src/models/rules/account-cluster-risk/current-investment.ts @@ -14,7 +14,7 @@ export class AccountClusterRiskCurrentInvestment extends Rule { private accounts: PortfolioDetails['accounts'] ) { super(exchangeRateDataService, { - name: 'Current Investment' + name: 'Investment' }); } @@ -28,7 +28,7 @@ export class AccountClusterRiskCurrentInvestment extends Rule { for (const [accountId, account] of Object.entries(this.accounts)) { accounts[accountId] = { name: account.name, - investment: account.current + investment: account.valueInBaseCurrency }; } diff --git a/apps/api/src/models/rules/account-cluster-risk/initial-investment.ts b/apps/api/src/models/rules/account-cluster-risk/initial-investment.ts deleted file mode 100644 index 607f71001..000000000 --- a/apps/api/src/models/rules/account-cluster-risk/initial-investment.ts +++ /dev/null @@ -1,88 +0,0 @@ -import { RuleSettings } from '@ghostfolio/api/models/interfaces/rule-settings.interface'; -import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; -import { - PortfolioDetails, - PortfolioPosition, - UserSettings -} from '@ghostfolio/common/interfaces'; - -import { Rule } from '../../rule'; - -export class AccountClusterRiskInitialInvestment extends Rule { - public constructor( - protected exchangeRateDataService: ExchangeRateDataService, - private accounts: PortfolioDetails['accounts'] - ) { - super(exchangeRateDataService, { - name: 'Initial Investment' - }); - } - - public evaluate(ruleSettings?: Settings) { - const accounts: { - [symbol: string]: Pick & { - investment: number; - }; - } = {}; - - for (const [accountId, account] of Object.entries(this.accounts)) { - accounts[accountId] = { - name: account.name, - investment: account.original - }; - } - - let maxItem; - let totalInvestment = 0; - - for (const account of Object.values(accounts)) { - if (!maxItem) { - maxItem = account; - } - - // Calculate total investment - totalInvestment += account.investment; - - // Find maximum - if (account.investment > maxItem?.investment) { - maxItem = account; - } - } - - const maxInvestmentRatio = maxItem.investment / totalInvestment; - - if (maxInvestmentRatio > ruleSettings.threshold) { - return { - evaluation: `Over ${ - ruleSettings.threshold * 100 - }% of your initial investment is at ${maxItem.name} (${( - maxInvestmentRatio * 100 - ).toPrecision(3)}%)`, - value: false - }; - } - - return { - evaluation: `The major part of your initial investment is at ${ - maxItem.name - } (${(maxInvestmentRatio * 100).toPrecision(3)}%) and does not exceed ${ - ruleSettings.threshold * 100 - }%`, - value: true - }; - } - - public getSettings(aUserSettings: UserSettings): Settings { - return { - baseCurrency: aUserSettings.baseCurrency, - isActive: true, - threshold: 0.5 - }; - } -} - -interface Settings extends RuleSettings { - baseCurrency: string; - isActive: boolean; - threshold: number; -} diff --git a/apps/api/src/models/rules/currency-cluster-risk/base-currency-current-investment.ts b/apps/api/src/models/rules/currency-cluster-risk/base-currency-current-investment.ts index 64f8852fe..2facb8803 100644 --- a/apps/api/src/models/rules/currency-cluster-risk/base-currency-current-investment.ts +++ b/apps/api/src/models/rules/currency-cluster-risk/base-currency-current-investment.ts @@ -10,7 +10,7 @@ export class CurrencyClusterRiskBaseCurrencyCurrentInvestment extends Rule { - public constructor( - protected exchangeRateDataService: ExchangeRateDataService, - private positions: TimelinePosition[] - ) { - super(exchangeRateDataService, { - name: 'Initial Investment: Base Currency' - }); - } - - public evaluate(ruleSettings: Settings) { - const positionsGroupedByCurrency = this.groupCurrentPositionsByAttribute( - this.positions, - 'currency', - ruleSettings.baseCurrency - ); - - let maxItem = positionsGroupedByCurrency[0]; - let totalInvestment = 0; - - positionsGroupedByCurrency.forEach((groupItem) => { - // Calculate total investment - totalInvestment += groupItem.investment; - - // Find maximum - if (groupItem.investment > maxItem.investment) { - maxItem = groupItem; - } - }); - - const baseCurrencyItem = positionsGroupedByCurrency.find((item) => { - return item.groupKey === ruleSettings.baseCurrency; - }); - - const baseCurrencyInvestmentRatio = - baseCurrencyItem?.investment / totalInvestment || 0; - - if (maxItem.groupKey !== ruleSettings.baseCurrency) { - return { - evaluation: `The major part of your initial investment is not in your base currency (${( - baseCurrencyInvestmentRatio * 100 - ).toPrecision(3)}% in ${ruleSettings.baseCurrency})`, - value: false - }; - } - - return { - evaluation: `The major part of your initial investment is in your base currency (${( - baseCurrencyInvestmentRatio * 100 - ).toPrecision(3)}% in ${ruleSettings.baseCurrency})`, - value: true - }; - } - - public getSettings(aUserSettings: UserSettings): Settings { - return { - baseCurrency: aUserSettings.baseCurrency, - isActive: true - }; - } -} - -interface Settings extends RuleSettings { - baseCurrency: string; -} diff --git a/apps/api/src/models/rules/currency-cluster-risk/current-investment.ts b/apps/api/src/models/rules/currency-cluster-risk/current-investment.ts index 2a86ed6f6..2d69865f5 100644 --- a/apps/api/src/models/rules/currency-cluster-risk/current-investment.ts +++ b/apps/api/src/models/rules/currency-cluster-risk/current-investment.ts @@ -10,7 +10,7 @@ export class CurrencyClusterRiskCurrentInvestment extends Rule { private positions: TimelinePosition[] ) { super(exchangeRateDataService, { - name: 'Current Investment' + name: 'Investment' }); } diff --git a/apps/api/src/models/rules/currency-cluster-risk/initial-investment.ts b/apps/api/src/models/rules/currency-cluster-risk/initial-investment.ts deleted file mode 100644 index 65009e346..000000000 --- a/apps/api/src/models/rules/currency-cluster-risk/initial-investment.ts +++ /dev/null @@ -1,72 +0,0 @@ -import { RuleSettings } from '@ghostfolio/api/models/interfaces/rule-settings.interface'; -import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; -import { TimelinePosition, UserSettings } from '@ghostfolio/common/interfaces'; - -import { Rule } from '../../rule'; - -export class CurrencyClusterRiskInitialInvestment extends Rule { - public constructor( - protected exchangeRateDataService: ExchangeRateDataService, - private positions: TimelinePosition[] - ) { - super(exchangeRateDataService, { - name: 'Initial Investment' - }); - } - - public evaluate(ruleSettings: Settings) { - const positionsGroupedByCurrency = this.groupCurrentPositionsByAttribute( - this.positions, - 'currency', - ruleSettings.baseCurrency - ); - - let maxItem = positionsGroupedByCurrency[0]; - let totalInvestment = 0; - - positionsGroupedByCurrency.forEach((groupItem) => { - // Calculate total investment - totalInvestment += groupItem.investment; - - // Find maximum - if (groupItem.investment > maxItem.investment) { - maxItem = groupItem; - } - }); - - const maxInvestmentRatio = maxItem.investment / totalInvestment; - - if (maxInvestmentRatio > ruleSettings.threshold) { - return { - evaluation: `Over ${ - ruleSettings.threshold * 100 - }% of your initial investment is in ${maxItem.groupKey} (${( - maxInvestmentRatio * 100 - ).toPrecision(3)}%)`, - value: false - }; - } - - return { - evaluation: `The major part of your initial investment is in ${ - maxItem.groupKey - } (${(maxInvestmentRatio * 100).toPrecision(3)}%) and does not exceed ${ - ruleSettings.threshold * 100 - }%`, - value: true - }; - } - - public getSettings(aUserSettings: UserSettings): Settings { - return { - baseCurrency: aUserSettings.baseCurrency, - isActive: true, - threshold: 0.5 - }; - } -} - -interface Settings extends RuleSettings { - baseCurrency: string; - threshold: number; -} diff --git a/apps/api/src/models/rules/fees/fee-ratio-initial-investment.ts b/apps/api/src/models/rules/fees/fee-ratio-initial-investment.ts index 90f26198f..dfe375c43 100644 --- a/apps/api/src/models/rules/fees/fee-ratio-initial-investment.ts +++ b/apps/api/src/models/rules/fees/fee-ratio-initial-investment.ts @@ -11,7 +11,7 @@ export class FeeRatioInitialInvestment extends Rule { private fees: number ) { super(exchangeRateDataService, { - name: 'Initial Investment' + name: 'Investment' }); } 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 7d541553f..6b5baa3ef 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 @@ -249,13 +249,22 @@ export class AllocationsPageComponent implements OnDestroy, OnInit { public initializeAnalysisData() { this.initialize(); - for (const [id, { current, name }] of Object.entries( - this.portfolioDetails.accounts - )) { + for (const [ + id, + { name, valueInBaseCurrency, valueInPercentage } + ] of Object.entries(this.portfolioDetails.accounts)) { + let value = 0; + + if (this.hasImpersonationId) { + value = valueInPercentage; + } else { + value = valueInBaseCurrency; + } + this.accounts[id] = { id, name, - value: current + value }; } diff --git a/libs/common/src/lib/interfaces/portfolio-details.interface.ts b/libs/common/src/lib/interfaces/portfolio-details.interface.ts index d17e4c75e..2fd07dc71 100644 --- a/libs/common/src/lib/interfaces/portfolio-details.interface.ts +++ b/libs/common/src/lib/interfaces/portfolio-details.interface.ts @@ -8,9 +8,9 @@ export interface PortfolioDetails { [id: string]: { balance: number; currency: string; - current: number; name: string; - original: number; + valueInBaseCurrency: number; + valueInPercentage?: number; }; }; filteredValueInBaseCurrency?: number;